LayoutTests:
[WebKit-https.git] / WebCore / bridge / mac / FrameMac.mm
1 /*
2  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "config.h"
27 #import "FrameMac.h"
28
29 #import "BrowserExtensionMac.h"
30 #import "Cache.h"
31 #import "Cursor.h"
32 #import "DOMInternal.h"
33 #import "EventNames.h"
34 #import "FloatRect.h"
35 #import "FoundationExtras.h"
36 #import "FramePrivate.h"
37 #import "FrameView.h"
38 #import "GraphicsContext.h"
39 #import "HTMLFormElement.h"
40 #import "HTMLGenericFormElement.h"
41 #import "InlineTextBox.h"
42 #import "AccessibilityObjectCache.h"
43 #import "ClipboardMac.h"
44 #import "KWQEditCommand.h"
45 #import "BlockExceptions.h"
46 #import "KWQFormData.h"
47 #import "TransferJob.h"
48 #import "Logging.h"
49 #import "KWQPageState.h"
50 #import "RegularExpression.h"
51 #import "KWQScrollBar.h"
52 #import "TextEncoding.h"
53 #import "PlatformKeyboardEvent.h"
54 #import "PlatformMouseEvent.h"
55 #import "MouseEventWithHitTestResults.h"
56 #import "Plugin.h"
57 #import "RenderTableCell.h"
58 #import "SelectionController.h"
59 #import "VisiblePosition.h"
60 #import "WebCoreFrameBridge.h"
61 #import "WebCoreGraphicsBridge.h"
62 #import "WebCoreViewFactory.h"
63 #import "WebDashboardRegion.h"
64 #import "PlatformWheelEvent.h"
65 #import "CSSComputedStyleDeclaration.h"
66 #import "csshelper.h"
67 #import "dom2_eventsimpl.h"
68 #import "Range.h"
69 #import "Position.h"
70 #import "HTMLDocument.h"
71 #import "html_tableimpl.h"
72 #import "kjs_binding.h"
73 #import "kjs_window.h"
74 #import "RenderCanvas.h"
75 #import "render_frames.h"
76 #import "RenderImage.h"
77 #import "render_list.h"
78 #import "render_style.h"
79 #import "RenderTheme.h"
80 #import "TextIterator.h"
81 #import "visible_units.h"
82 #import <JavaScriptCore/NP_jsobject.h>
83 #import <JavaScriptCore/WebScriptObjectPrivate.h>
84 #import <JavaScriptCore/identifier.h>
85 #import <JavaScriptCore/interpreter.h>
86 #import <JavaScriptCore/npruntime_impl.h>
87 #import <JavaScriptCore/property_map.h>
88 #import <JavaScriptCore/runtime.h>
89 #import <JavaScriptCore/runtime_root.h>
90
91 #undef _KWQ_TIMING
92
93 @interface NSObject (WebPlugIn)
94 - (id)objectForWebScript;
95 - (void *)pluginScriptableObject;
96 @end
97
98 using namespace KJS;
99 using namespace Bindings;
100
101 using namespace KIO;
102
103 namespace WebCore {
104
105 using namespace EventNames;
106 using namespace HTMLNames;
107
108 NSEvent *FrameMac::_currentEvent = nil;
109
110 bool FrameView::isFrameView() const
111 {
112     return true;
113 }
114
115 FrameMac::FrameMac(Page* page, RenderPart* ownerRenderer)
116     : Frame(page, ownerRenderer)
117     , _bridge(nil)
118     , _mouseDownView(nil)
119     , _sendingEventToSubview(false)
120     , _mouseDownMayStartDrag(false)
121     , _mouseDownMayStartSelect(false)
122     , _activationEventNumber(0)
123     , _formValuesAboutToBeSubmitted(nil)
124     , _formAboutToBeSubmitted(nil)
125     , _bindingRoot(0)
126     , _windowScriptObject(0)
127     , _windowScriptNPObject(0)
128 {
129     d->m_extension = new BrowserExtensionMac(this);
130 }
131
132 FrameMac::~FrameMac()
133 {
134     setView(0);
135     freeClipboard();
136     clearRecordedFormValues();    
137     
138     [_bridge clearFrame];
139     KWQRelease(_bridge);
140     _bridge = nil;
141 }
142
143 void FrameMac::freeClipboard()
144 {
145     if (_dragClipboard)
146         _dragClipboard->setAccessPolicy(ClipboardMac::Numb);
147 }
148
149 bool FrameMac::openURL(const KURL &url)
150 {
151     BEGIN_BLOCK_OBJC_EXCEPTIONS;
152
153     // FIXME: The lack of args here to get the reload flag from
154     // indicates a problem in how we use Frame::processObjectRequest,
155     // where we are opening the URL before the args are set up.
156     [_bridge loadURL:url.getNSURL()
157             referrer:[_bridge referrer]
158               reload:NO
159          userGesture:userGestureHint()
160               target:nil
161      triggeringEvent:nil
162                 form:nil
163           formValues:nil];
164
165     END_BLOCK_OBJC_EXCEPTIONS;
166
167     return true;
168 }
169
170 void FrameMac::openURLRequest(const ResourceRequest& request)
171 {
172     BEGIN_BLOCK_OBJC_EXCEPTIONS;
173
174     NSString *referrer;
175     String argsReferrer = request.referrer();
176     if (!argsReferrer.isEmpty())
177         referrer = argsReferrer;
178     else
179         referrer = [_bridge referrer];
180
181     [_bridge loadURL:request.url().getNSURL()
182             referrer:referrer
183               reload:request.reload
184          userGesture:userGestureHint()
185               target:request.frameName
186      triggeringEvent:nil
187                 form:nil
188           formValues:nil];
189
190     END_BLOCK_OBJC_EXCEPTIONS;
191 }
192
193
194 // Either get cached regexp or build one that matches any of the labels.
195 // The regexp we build is of the form:  (STR1|STR2|STRN)
196 RegularExpression *regExpForLabels(NSArray *labels)
197 {
198     // All the ObjC calls in this method are simple array and string
199     // calls which we can assume do not raise exceptions
200
201
202     // Parallel arrays that we use to cache regExps.  In practice the number of expressions
203     // that the app will use is equal to the number of locales is used in searching.
204     static const unsigned int regExpCacheSize = 4;
205     static NSMutableArray *regExpLabels = nil;
206     static Vector<RegularExpression*> regExps;
207     static RegularExpression wordRegExp = RegularExpression("\\w");
208
209     RegularExpression *result;
210     if (!regExpLabels)
211         regExpLabels = [[NSMutableArray alloc] initWithCapacity:regExpCacheSize];
212     unsigned int cacheHit = [regExpLabels indexOfObject:labels];
213     if (cacheHit != NSNotFound)
214         result = regExps.at(cacheHit);
215     else {
216         DeprecatedString pattern("(");
217         unsigned int numLabels = [labels count];
218         unsigned int i;
219         for (i = 0; i < numLabels; i++) {
220             DeprecatedString label = DeprecatedString::fromNSString((NSString *)[labels objectAtIndex:i]);
221
222             bool startsWithWordChar = false;
223             bool endsWithWordChar = false;
224             if (label.length() != 0) {
225                 startsWithWordChar = wordRegExp.search(label.at(0)) >= 0;
226                 endsWithWordChar = wordRegExp.search(label.at(label.length() - 1)) >= 0;
227             }
228             
229             if (i != 0)
230                 pattern.append("|");
231             // Search for word boundaries only if label starts/ends with "word characters".
232             // If we always searched for word boundaries, this wouldn't work for languages
233             // such as Japanese.
234             if (startsWithWordChar) {
235                 pattern.append("\\b");
236             }
237             pattern.append(label);
238             if (endsWithWordChar) {
239                 pattern.append("\\b");
240             }
241         }
242         pattern.append(")");
243         result = new RegularExpression(pattern, false);
244     }
245
246     // add regexp to the cache, making sure it is at the front for LRU ordering
247     if (cacheHit != 0) {
248         if (cacheHit != NSNotFound) {
249             // remove from old spot
250             [regExpLabels removeObjectAtIndex:cacheHit];
251             regExps.remove(cacheHit);
252         }
253         // add to start
254         [regExpLabels insertObject:labels atIndex:0];
255         regExps.insert(0, result);
256         // trim if too big
257         if ([regExpLabels count] > regExpCacheSize) {
258             [regExpLabels removeObjectAtIndex:regExpCacheSize];
259             RegularExpression *last = regExps.last();
260             regExps.removeLast();
261             delete last;
262         }
263     }
264     return result;
265 }
266
267 NSString *FrameMac::searchForLabelsAboveCell(RegularExpression *regExp, HTMLTableCellElement *cell)
268 {
269     RenderTableCell *cellRenderer = static_cast<RenderTableCell *>(cell->renderer());
270
271     if (cellRenderer && cellRenderer->isTableCell()) {
272         RenderTableCell *cellAboveRenderer = cellRenderer->table()->cellAbove(cellRenderer);
273
274         if (cellAboveRenderer) {
275             HTMLTableCellElement *aboveCell =
276                 static_cast<HTMLTableCellElement *>(cellAboveRenderer->element());
277
278             if (aboveCell) {
279                 // search within the above cell we found for a match
280                 for (Node *n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) {
281                     if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
282                         // For each text chunk, run the regexp
283                         DeprecatedString nodeString = n->nodeValue().deprecatedString();
284                         int pos = regExp->searchRev(nodeString);
285                         if (pos >= 0)
286                             return nodeString.mid(pos, regExp->matchedLength()).getNSString();
287                     }
288                 }
289             }
290         }
291     }
292     // Any reason in practice to search all cells in that are above cell?
293     return nil;
294 }
295
296 NSString *FrameMac::searchForLabelsBeforeElement(NSArray *labels, Element *element)
297 {
298     RegularExpression *regExp = regExpForLabels(labels);
299     // We stop searching after we've seen this many chars
300     const unsigned int charsSearchedThreshold = 500;
301     // This is the absolute max we search.  We allow a little more slop than
302     // charsSearchedThreshold, to make it more likely that we'll search whole nodes.
303     const unsigned int maxCharsSearched = 600;
304     // If the starting element is within a table, the cell that contains it
305     HTMLTableCellElement *startingTableCell = 0;
306     bool searchedCellAbove = false;
307
308     // walk backwards in the node tree, until another element, or form, or end of tree
309     int unsigned lengthSearched = 0;
310     Node *n;
311     for (n = element->traversePreviousNode();
312          n && lengthSearched < charsSearchedThreshold;
313          n = n->traversePreviousNode())
314     {
315         if (n->hasTagName(formTag)
316             || (n->isHTMLElement()
317                 && static_cast<HTMLElement *>(n)->isGenericFormElement()))
318         {
319             // We hit another form element or the start of the form - bail out
320             break;
321         } else if (n->hasTagName(tdTag) && !startingTableCell) {
322             startingTableCell = static_cast<HTMLTableCellElement *>(n);
323         } else if (n->hasTagName(trTag) && startingTableCell) {
324             NSString *result = searchForLabelsAboveCell(regExp, startingTableCell);
325             if (result) {
326                 return result;
327             }
328             searchedCellAbove = true;
329         } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
330             // For each text chunk, run the regexp
331             DeprecatedString nodeString = n->nodeValue().deprecatedString();
332             // add 100 for slop, to make it more likely that we'll search whole nodes
333             if (lengthSearched + nodeString.length() > maxCharsSearched)
334                 nodeString = nodeString.right(charsSearchedThreshold - lengthSearched);
335             int pos = regExp->searchRev(nodeString);
336             if (pos >= 0)
337                 return nodeString.mid(pos, regExp->matchedLength()).getNSString();
338             else
339                 lengthSearched += nodeString.length();
340         }
341     }
342
343     // If we started in a cell, but bailed because we found the start of the form or the
344     // previous element, we still might need to search the row above us for a label.
345     if (startingTableCell && !searchedCellAbove) {
346          return searchForLabelsAboveCell(regExp, startingTableCell);
347     } else {
348         return nil;
349     }
350 }
351
352 NSString *FrameMac::matchLabelsAgainstElement(NSArray *labels, Element *element)
353 {
354     DeprecatedString name = element->getAttribute(nameAttr).deprecatedString();
355     // Make numbers and _'s in field names behave like word boundaries, e.g., "address2"
356     name.replace(RegularExpression("[[:digit:]]"), " ");
357     name.replace('_', ' ');
358     
359     RegularExpression *regExp = regExpForLabels(labels);
360     // Use the largest match we can find in the whole name string
361     int pos;
362     int length;
363     int bestPos = -1;
364     int bestLength = -1;
365     int start = 0;
366     do {
367         pos = regExp->search(name, start);
368         if (pos != -1) {
369             length = regExp->matchedLength();
370             if (length >= bestLength) {
371                 bestPos = pos;
372                 bestLength = length;
373             }
374             start = pos+1;
375         }
376     } while (pos != -1);
377
378     if (bestPos != -1)
379         return name.mid(bestPos, bestLength).getNSString();
380     return nil;
381 }
382
383 // Searches from the beginning of the document if nothing is selected.
384 bool FrameMac::findString(NSString *string, bool forward, bool caseFlag, bool wrapFlag)
385 {
386     String target = string;
387     if (target.isEmpty())
388         return false;
389     
390     // Initially search from the start (if forward) or end (if backward) of the selection, and search to edge of document.
391     RefPtr<Range> searchRange(rangeOfContents(document()));
392     if (selection().start().node()) {
393         if (forward)
394             setStart(searchRange.get(), VisiblePosition(selection().start(), selection().affinity()));
395         else
396             setEnd(searchRange.get(), VisiblePosition(selection().end(), selection().affinity()));
397     }
398     RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, forward, caseFlag));
399     
400     // If we re-found the (non-empty) selected range, then search again starting just past the selected range.
401     if (selection().start().node() && *resultRange == *selection().toRange()) {
402         searchRange = rangeOfContents(document());
403         if (forward)
404             setStart(searchRange.get(), VisiblePosition(selection().end(), selection().affinity()));
405         else
406             setEnd(searchRange.get(), VisiblePosition(selection().start(), selection().affinity()));
407         resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
408     }
409     
410     int exception = 0;
411     
412     // if we didn't find anything and we're wrapping, search again in the entire document (this will
413     // redundantly re-search the area already searched in some cases).
414     if (resultRange->collapsed(exception) && wrapFlag) {
415         searchRange = rangeOfContents(document());
416         resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
417         // We used to return false here if we ended up with the same range that we started with
418         // (e.g., the selection was already the only instance of this text). But we decided that
419         // this should be a success case instead, so we'll just fall through in that case.
420     }
421
422     if (resultRange->collapsed(exception))
423         return false;
424
425     setSelection(SelectionController(resultRange.get(), DOWNSTREAM));
426     revealSelection();
427     return true;
428 }
429
430 void FrameMac::submitForm(const ResourceRequest& request)
431 {
432     BEGIN_BLOCK_OBJC_EXCEPTIONS;
433
434     // FIXME: We'd like to remove this altogether and fix the multiple form submission issue another way.
435     // We do not want to submit more than one form from the same page,
436     // nor do we want to submit a single form more than once.
437     // This flag prevents these from happening; not sure how other browsers prevent this.
438     // The flag is reset in each time we start handle a new mouse or key down event, and
439     // also in setView since this part may get reused for a page from the back/forward cache.
440     // The form multi-submit logic here is only needed when we are submitting a form that affects this frame.
441     // FIXME: Frame targeting is only one of the ways the submission could end up doing something other
442     // than replacing this frame's content, so this check is flawed. On the other hand, the check is hardly
443     // needed any more now that we reset d->m_submittedFormURL on each mouse or key down event.
444     WebCoreFrameBridge *target = request.frameName.isEmpty() ? _bridge : [_bridge findFrameNamed:request.frameName];
445     Frame *targetPart = [target impl];
446     bool willReplaceThisFrame = false;
447     for (Frame *p = this; p; p = p->tree()->parent()) {
448         if (p == targetPart) {
449             willReplaceThisFrame = true;
450             break;
451         }
452     }
453     if (willReplaceThisFrame) {
454         if (d->m_submittedFormURL == request.url())
455             return;
456         d->m_submittedFormURL = request.url();
457     }
458
459     if (!request.doPost()) {
460         [_bridge loadURL:request.url().getNSURL()
461                 referrer:[_bridge referrer] 
462                   reload:request.reload
463              userGesture:true
464                   target:request.frameName
465          triggeringEvent:_currentEvent
466                     form:_formAboutToBeSubmitted
467               formValues:_formValuesAboutToBeSubmitted];
468     } else {
469         ASSERT(request.contentType().startsWith("Content-Type: "));
470         [_bridge postWithURL:request.url().getNSURL()
471                     referrer:[_bridge referrer] 
472                       target:request.frameName
473                         data:arrayFromFormData(request.postData)
474                  contentType:request.contentType().substring(14)
475              triggeringEvent:_currentEvent
476                         form:_formAboutToBeSubmitted
477                   formValues:_formValuesAboutToBeSubmitted];
478     }
479     clearRecordedFormValues();
480
481     END_BLOCK_OBJC_EXCEPTIONS;
482 }
483
484 void FrameMac::frameDetached()
485 {
486     Frame::frameDetached();
487
488     BEGIN_BLOCK_OBJC_EXCEPTIONS;
489     [Mac(this)->bridge() frameDetached];
490     END_BLOCK_OBJC_EXCEPTIONS;
491 }
492
493 void FrameMac::urlSelected(const ResourceRequest& request)
494 {
495     BEGIN_BLOCK_OBJC_EXCEPTIONS;
496
497     NSString* referrer;
498     String argsReferrer = request.referrer();
499     if (!argsReferrer.isEmpty())
500         referrer = argsReferrer;
501     else
502         referrer = [_bridge referrer];
503
504     [_bridge loadURL:request.url().getNSURL()
505             referrer:referrer
506               reload:request.reload
507          userGesture:true
508               target:request.frameName
509      triggeringEvent:_currentEvent
510                 form:nil
511           formValues:nil];
512
513     END_BLOCK_OBJC_EXCEPTIONS;
514 }
515
516 ObjectContentType FrameMac::objectContentType(const KURL& url, const String& mimeType)
517 {
518     return (ObjectContentType)[_bridge determineObjectFromMIMEType:mimeType URL:url.getNSURL()];
519 }
520
521 static NSArray* nsArray(const Vector<String>& vector)
522 {
523     unsigned len = vector.size();
524     NSMutableArray* array = [NSMutableArray arrayWithCapacity:len];
525     for (unsigned x = 0; x < len; x++)
526         [array addObject:vector[x]];
527     return array;
528 }
529
530 Plugin* FrameMac::createPlugin(const KURL& url, const Vector<String>& paramNames, const Vector<String>& paramValues, const String& mimeType)
531 {
532     BEGIN_BLOCK_OBJC_EXCEPTIONS;
533
534     return new Plugin(new Widget([_bridge viewForPluginWithURL:url.getNSURL()
535                                   attributeNames:nsArray(paramNames)
536                                   attributeValues:nsArray(paramValues)
537                                   MIMEType:mimeType]));
538
539     END_BLOCK_OBJC_EXCEPTIONS;
540     return 0;
541 }
542
543
544 Frame* FrameMac::createFrame(const KURL& url, const String& name, RenderPart* renderer, const String& referrer)
545 {
546     BEGIN_BLOCK_OBJC_EXCEPTIONS;
547     
548     BOOL allowsScrolling = YES;
549     int marginWidth = -1;
550     int marginHeight = -1;
551     if (renderer->element()->hasTagName(frameTag) || renderer->element()->hasTagName(iframeTag)) {
552         HTMLFrameElement *o = static_cast<HTMLFrameElement *>(renderer->element());
553         allowsScrolling = o->scrollingMode() != ScrollBarAlwaysOff;
554         marginWidth = o->getMarginWidth();
555         marginHeight = o->getMarginHeight();
556     }
557
558     WebCoreFrameBridge *childBridge = [_bridge createChildFrameNamed:name
559                                                              withURL:url.getNSURL()
560                                                             referrer:referrer 
561                                                           renderPart:renderer
562                                                      allowsScrolling:allowsScrolling
563                                                          marginWidth:marginWidth
564                                                         marginHeight:marginHeight];
565     return [childBridge impl];
566
567     END_BLOCK_OBJC_EXCEPTIONS;
568     return 0;
569 }
570
571 void FrameMac::setView(FrameView *view)
572 {
573     // Detach the document now, so any onUnload handlers get run - if
574     // we wait until the view is destroyed, then things won't be
575     // hooked up enough for some JavaScript calls to work.
576     if (d->m_doc && view == 0)
577         d->m_doc->detach();
578     
579     d->m_view = view;
580     
581     // Delete old PlugIn data structures
582     cleanupPluginRootObjects();
583     _bindingRoot = 0;
584     KWQRelease(_windowScriptObject);
585     _windowScriptObject = 0;
586
587     if (_windowScriptNPObject) {
588         // Call _NPN_DeallocateObject() instead of _NPN_ReleaseObject() so that we don't leak if a plugin fails to release the window
589         // script object properly.
590         // This shouldn't cause any problems for plugins since they should have already been stopped and destroyed at this point.
591         _NPN_DeallocateObject(_windowScriptNPObject);
592         _windowScriptNPObject = 0;
593     }
594
595     // Only one form submission is allowed per view of a part.
596     // Since this part may be getting reused as a result of being
597     // pulled from the back/forward cache, reset this flag.
598     d->m_submittedFormURL = KURL();
599 }
600
601 void FrameMac::setTitle(const String &title)
602 {
603     String text = title;
604     text.replace(QChar('\\'), backslashAsCurrencySymbol());
605
606     BEGIN_BLOCK_OBJC_EXCEPTIONS;
607     [_bridge setTitle:text];
608     END_BLOCK_OBJC_EXCEPTIONS;
609 }
610
611 void FrameMac::setStatusBarText(const String& status)
612 {
613     String text = status;
614     text.replace(QChar('\\'), backslashAsCurrencySymbol());
615     
616     BEGIN_BLOCK_OBJC_EXCEPTIONS;
617     [_bridge setStatusText:text];
618     END_BLOCK_OBJC_EXCEPTIONS;
619 }
620
621 void FrameMac::scheduleClose()
622 {
623     if (!shouldClose())
624         return;
625     BEGIN_BLOCK_OBJC_EXCEPTIONS;
626     [_bridge closeWindowSoon];
627     END_BLOCK_OBJC_EXCEPTIONS;
628 }
629
630 void FrameMac::unfocusWindow()
631 {
632     BEGIN_BLOCK_OBJC_EXCEPTIONS;
633     [_bridge unfocusWindow];
634     END_BLOCK_OBJC_EXCEPTIONS;
635 }
636
637 String FrameMac::advanceToNextMisspelling(bool startBeforeSelection)
638 {
639     int exception = 0;
640
641     // The basic approach is to search in two phases - from the selection end to the end of the doc, and
642     // then we wrap and search from the doc start to (approximately) where we started.
643     
644     // Start at the end of the selection, search to edge of document.  Starting at the selection end makes
645     // repeated "check spelling" commands work.
646     RefPtr<Range> searchRange(rangeOfContents(document()));
647     bool startedWithSelection = false;
648     if (selection().start().node()) {
649         startedWithSelection = true;
650         if (startBeforeSelection) {
651             VisiblePosition start(selection().start(), selection().affinity());
652             // We match AppKit's rule: Start 1 character before the selection.
653             VisiblePosition oneBeforeStart = start.previous();
654             setStart(searchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start);
655         } else
656             setStart(searchRange.get(), VisiblePosition(selection().end(), selection().affinity()));
657     }
658
659     // If we're not in an editable node, try to find one, make that our range to work in
660     Node *editableNode = searchRange->startContainer(exception);
661     if (!editableNode->isContentEditable()) {
662         editableNode = editableNode->nextEditable();
663         if (!editableNode) {
664             return String();
665         }
666         searchRange->setStartBefore(editableNode, exception);
667         startedWithSelection = false;   // won't need to wrap
668     }
669     
670     // topNode defines the whole range we want to operate on 
671     Node *topNode = editableNode->rootEditableElement();
672     searchRange->setEndAfter(topNode, exception);
673
674     // Make sure start of searchRange is not in the middle of a word.  Jumping back a char and then
675     // forward by a word happens to do the trick.
676     if (startedWithSelection) {
677         VisiblePosition oneBeforeStart = startVisiblePosition(searchRange.get(), DOWNSTREAM).previous();
678         if (oneBeforeStart.isNotNull()) {
679             setStart(searchRange.get(), endOfWord(oneBeforeStart));
680         } // else we were already at the start of the editable node
681     }
682     
683     if (searchRange->collapsed(exception))
684         return String();       // nothing to search in
685     
686     // Get the spell checker if it is available
687     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
688     if (checker == nil)
689         return String();
690         
691     WordAwareIterator it(searchRange.get());
692     bool wrapped = false;
693     
694     // We go to the end of our first range instead of the start of it, just to be sure
695     // we don't get foiled by any word boundary problems at the start.  It means we might
696     // do a tiny bit more searching.
697     Node *searchEndAfterWrapNode = it.range()->endContainer(exception);
698     int searchEndAfterWrapOffset = it.range()->endOffset(exception);
699
700     while (1) {
701         if (!it.atEnd()) {      // we may be starting at the end of the doc, and already by atEnd
702             const QChar *chars = it.characters();
703             int len = it.length();
704             if (len > 1 || !chars[0].isSpace()) {
705                 NSString *chunk = [[NSString alloc] initWithCharactersNoCopy:(unichar *)chars length:len freeWhenDone:NO];
706                 NSRange misspelling = [checker checkSpellingOfString:chunk startingAt:0 language:nil wrap:NO inSpellDocumentWithTag:[_bridge spellCheckerDocumentTag] wordCount:NULL];
707                 [chunk release];
708                 if (misspelling.length > 0) {
709                     // Build up result range and string.  Note the misspelling may span many text nodes,
710                     // but the CharIterator insulates us from this complexity
711                     RefPtr<Range> misspellingRange(rangeOfContents(document()));
712                     CharacterIterator chars(it.range().get());
713                     chars.advance(misspelling.location);
714                     misspellingRange->setStart(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
715                     DeprecatedString result = chars.string(misspelling.length);
716                     misspellingRange->setEnd(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
717
718                     setSelection(SelectionController(misspellingRange.get(), DOWNSTREAM));
719                     revealSelection();
720                     // Mark misspelling in document.
721                     document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
722                     return result;
723                 }
724             }
725         
726             it.advance();
727         }
728         if (it.atEnd()) {
729             if (wrapped || !startedWithSelection) {
730                 break;      // finished the second range, or we did the whole doc with the first range
731             } else {
732                 // we've gone from the selection to the end of doc, now wrap around
733                 wrapped = YES;
734                 searchRange->setStartBefore(topNode, exception);
735                 // going until the end of the very first chunk we tested is far enough
736                 searchRange->setEnd(searchEndAfterWrapNode, searchEndAfterWrapOffset, exception);
737                 it = WordAwareIterator(searchRange.get());
738             }
739         }   
740     }
741     
742     return String();
743 }
744
745 bool FrameMac::wheelEvent(NSEvent *event)
746 {
747     FrameView *v = d->m_view.get();
748
749     if (v) {
750         NSEvent *oldCurrentEvent = _currentEvent;
751         _currentEvent = KWQRetain(event);
752
753         PlatformWheelEvent qEvent(event);
754         v->handleWheelEvent(qEvent);
755
756         ASSERT(_currentEvent == event);
757         KWQRelease(event);
758         _currentEvent = oldCurrentEvent;
759
760         if (qEvent.isAccepted())
761             return true;
762     }
763
764     // FIXME: The scrolling done here should be done in the default handlers
765     // of the elements rather than here in the part.
766
767     KWQScrollDirection direction;
768     float multiplier;
769     float deltaX = [event deltaX];
770     float deltaY = [event deltaY];
771     if (deltaX < 0) {
772         direction = KWQScrollRight;
773         multiplier = -deltaX;
774     } else if (deltaX > 0) {
775         direction = KWQScrollLeft;
776         multiplier = deltaX;
777     } else if (deltaY < 0) {
778         direction = KWQScrollDown;
779         multiplier = -deltaY;
780     }  else if (deltaY > 0) {
781         direction = KWQScrollUp;
782         multiplier = deltaY;
783     } else
784         return false;
785
786     RenderObject *r = renderer();
787     if (!r)
788         return false;
789     
790     NSPoint point = [d->m_view->getDocumentView() convertPoint:[event locationInWindow] fromView:nil];
791     RenderObject::NodeInfo nodeInfo(true, true);
792     r->layer()->hitTest(nodeInfo, IntPoint(point));    
793     
794     Node *node = nodeInfo.innerNode();
795     if (!node)
796         return false;
797     
798     r = node->renderer();
799     if (!r)
800         return false;
801     
802     return r->scroll(direction, KWQScrollWheel, multiplier);
803 }
804
805 void FrameMac::startRedirectionTimer()
806 {
807     stopRedirectionTimer();
808
809     Frame::startRedirectionTimer();
810
811     // Don't report history navigations, just actual redirection.
812     if (d->m_scheduledRedirection != historyNavigationScheduled) {
813         NSTimeInterval interval = d->m_redirectionTimer.nextFireInterval();
814         NSDate *fireDate = [[NSDate alloc] initWithTimeIntervalSinceNow:interval];
815         [_bridge reportClientRedirectToURL:KURL(d->m_redirectURL).getNSURL()
816                                      delay:d->m_delayRedirect
817                                   fireDate:fireDate
818                                lockHistory:d->m_redirectLockHistory
819                                isJavaScriptFormAction:d->m_executingJavaScriptFormAction];
820         [fireDate release];
821     }
822 }
823
824 void FrameMac::stopRedirectionTimer()
825 {
826     bool wasActive = d->m_redirectionTimer.isActive();
827
828     Frame::stopRedirectionTimer();
829
830     // Don't report history navigations, just actual redirection.
831     if (wasActive && d->m_scheduledRedirection != historyNavigationScheduled)
832         [_bridge reportClientRedirectCancelled:d->m_cancelWithLoadInProgress];
833 }
834
835 String FrameMac::userAgent() const
836 {
837     BEGIN_BLOCK_OBJC_EXCEPTIONS;
838     return [_bridge userAgentForURL:url().getNSURL()];
839     END_BLOCK_OBJC_EXCEPTIONS;
840          
841     return String();
842 }
843
844 String FrameMac::mimeTypeForFileName(const String& fileName) const
845 {
846     BEGIN_BLOCK_OBJC_EXCEPTIONS;
847     return [_bridge MIMETypeForPath:fileName];
848     END_BLOCK_OBJC_EXCEPTIONS;
849
850     return String();
851 }
852
853 NSView *FrameMac::nextKeyViewInFrame(Node *node, KWQSelectionDirection direction)
854 {
855     Document *doc = document();
856     if (!doc)
857         return nil;
858     
859     for (;;) {
860         node = direction == KWQSelectingNext
861             ? doc->nextFocusNode(node) : doc->previousFocusNode(node);
862         if (!node)
863             return nil;
864         
865         RenderObject *renderer = node->renderer();
866         if (renderer->isWidget()) {
867             RenderWidget *renderWidget = static_cast<RenderWidget *>(renderer);
868             Widget *widget = renderWidget->widget();
869             if (!widget)
870                 continue;
871             
872             FrameView *childFrameWidget = widget->isFrameView() ? static_cast<FrameView *>(widget) : 0;
873             NSView *view = nil;
874             if (childFrameWidget)
875                 view = Mac(childFrameWidget->frame())->nextKeyViewInFrame(0, direction);
876             else
877                 view = widget->getView();
878             if (view)
879                 return view;
880         } else
881             static_cast<Element *>(node)->focus(); 
882
883         [_bridge willMakeFirstResponderForNodeFocus];
884         return [_bridge documentView];
885     }
886 }
887
888 NSView *FrameMac::nextKeyViewInFrameHierarchy(Node *node, KWQSelectionDirection direction)
889 {
890     NSView *next = nextKeyViewInFrame(node, direction);
891     if (!next)
892         if (FrameMac *parent = Mac(tree()->parent()))
893             next = parent->nextKeyViewInFrameHierarchy(ownerElement(), direction);
894     
895     // remove focus from currently focused node if we're giving focus to another view
896     if (next && (next != [_bridge documentView]))
897         if (Document *doc = document())
898             doc->setFocusNode(0);
899     
900     return next;
901 }
902
903 NSView *FrameMac::nextKeyView(Node *node, KWQSelectionDirection direction)
904 {
905     NSView * next;
906     BEGIN_BLOCK_OBJC_EXCEPTIONS;
907
908     next = nextKeyViewInFrameHierarchy(node, direction);
909     if (next)
910         return next;
911
912     // Look at views from the top level part up, looking for a next key view that we can use.
913
914     next = direction == KWQSelectingNext
915         ? [_bridge nextKeyViewOutsideWebFrameViews]
916         : [_bridge previousKeyViewOutsideWebFrameViews];
917
918     if (next)
919         return next;
920
921     END_BLOCK_OBJC_EXCEPTIONS;
922     
923     // If all else fails, make a loop by starting from 0.
924     return nextKeyViewInFrameHierarchy(0, direction);
925 }
926
927 NSView *FrameMac::nextKeyViewForWidget(Widget *startingWidget, KWQSelectionDirection direction)
928 {
929     // Use the event filter object to figure out which RenderWidget owns this Widget and get to the DOM.
930     // Then get the next key view in the order determined by the DOM.
931     Node *node = nodeForWidget(startingWidget);
932     ASSERT(node);
933     return Mac(frameForNode(node))->nextKeyView(node, direction);
934 }
935
936 bool FrameMac::currentEventIsMouseDownInWidget(Widget *candidate)
937 {
938     BEGIN_BLOCK_OBJC_EXCEPTIONS;
939     switch ([[NSApp currentEvent] type]) {
940         case NSLeftMouseDown:
941         case NSRightMouseDown:
942         case NSOtherMouseDown:
943             break;
944         default:
945             return NO;
946     }
947     END_BLOCK_OBJC_EXCEPTIONS;
948     
949     Node *node = nodeForWidget(candidate);
950     ASSERT(node);
951     return frameForNode(node)->d->m_view->nodeUnderMouse() == node;
952 }
953
954 bool FrameMac::currentEventIsKeyboardOptionTab()
955 {
956     BEGIN_BLOCK_OBJC_EXCEPTIONS;
957     NSEvent *evt = [NSApp currentEvent];
958     if ([evt type] != NSKeyDown) {
959         return NO;
960     }
961
962     if (([evt modifierFlags] & NSAlternateKeyMask) == 0) {
963         return NO;
964     }
965     
966     NSString *chars = [evt charactersIgnoringModifiers];
967     if ([chars length] != 1)
968         return NO;
969     
970     const unichar tabKey = 0x0009;
971     const unichar shiftTabKey = 0x0019;
972     unichar c = [chars characterAtIndex:0];
973     if (c != tabKey && c != shiftTabKey)
974         return NO;
975     
976     END_BLOCK_OBJC_EXCEPTIONS;
977     return YES;
978 }
979
980 bool FrameMac::handleKeyboardOptionTabInView(NSView *view)
981 {
982     if (FrameMac::currentEventIsKeyboardOptionTab()) {
983         if (([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) != 0) {
984             [[view window] selectKeyViewPrecedingView:view];
985         } else {
986             [[view window] selectKeyViewFollowingView:view];
987         }
988         return YES;
989     }
990     
991     return NO;
992 }
993
994 bool FrameMac::tabsToLinks() const
995 {
996     if ([_bridge keyboardUIMode] & WebCoreKeyboardAccessTabsToLinks)
997         return !FrameMac::currentEventIsKeyboardOptionTab();
998     else
999         return FrameMac::currentEventIsKeyboardOptionTab();
1000 }
1001
1002 bool FrameMac::tabsToAllControls() const
1003 {
1004     WebCoreKeyboardUIMode keyboardUIMode = [_bridge keyboardUIMode];
1005     BOOL handlingOptionTab = FrameMac::currentEventIsKeyboardOptionTab();
1006
1007     // If tab-to-links is off, option-tab always highlights all controls
1008     if ((keyboardUIMode & WebCoreKeyboardAccessTabsToLinks) == 0 && handlingOptionTab) {
1009         return YES;
1010     }
1011     
1012     // If system preferences say to include all controls, we always include all controls
1013     if (keyboardUIMode & WebCoreKeyboardAccessFull) {
1014         return YES;
1015     }
1016     
1017     // Otherwise tab-to-links includes all controls, unless the sense is flipped via option-tab.
1018     if (keyboardUIMode & WebCoreKeyboardAccessTabsToLinks) {
1019         return !handlingOptionTab;
1020     }
1021     
1022     return handlingOptionTab;
1023 }
1024
1025 KJS::Bindings::RootObject *FrameMac::executionContextForDOM()
1026 {
1027     return bindingRootObject();
1028 }
1029
1030 KJS::Bindings::RootObject *FrameMac::bindingRootObject()
1031 {
1032     assert(jScriptEnabled());
1033     if (!_bindingRoot) {
1034         JSLock lock;
1035         _bindingRoot = new KJS::Bindings::RootObject(0);    // The root gets deleted by JavaScriptCore.
1036         KJS::JSObject *win = KJS::Window::retrieveWindow(this);
1037         _bindingRoot->setRootObjectImp (win);
1038         _bindingRoot->setInterpreter(jScript()->interpreter());
1039         addPluginRootObject (_bindingRoot);
1040     }
1041     return _bindingRoot;
1042 }
1043
1044 WebScriptObject *FrameMac::windowScriptObject()
1045 {
1046     if (!jScriptEnabled())
1047         return 0;
1048
1049     if (!_windowScriptObject) {
1050         KJS::JSLock lock;
1051         KJS::JSObject *win = KJS::Window::retrieveWindow(this);
1052         _windowScriptObject = KWQRetainNSRelease([[WebScriptObject alloc] _initWithJSObject:win originExecutionContext:bindingRootObject() executionContext:bindingRootObject()]);
1053     }
1054
1055     return _windowScriptObject;
1056 }
1057
1058 NPObject *FrameMac::windowScriptNPObject()
1059 {
1060     if (!_windowScriptNPObject) {
1061         if (jScriptEnabled()) {
1062             // JavaScript is enabled, so there is a JavaScript window object.  Return an NPObject bound to the window
1063             // object.
1064             KJS::JSObject *win = KJS::Window::retrieveWindow(this);
1065             assert(win);
1066             _windowScriptNPObject = _NPN_CreateScriptObject(0, win, bindingRootObject(), bindingRootObject());
1067         } else {
1068             // JavaScript is not enabled, so we cannot bind the NPObject to the JavaScript window object.
1069             // Instead, we create an NPObject of a different class, one which is not bound to a JavaScript object.
1070             _windowScriptNPObject = _NPN_CreateNoScriptObject();
1071         }
1072     }
1073
1074     return _windowScriptNPObject;
1075 }
1076
1077 void FrameMac::partClearedInBegin()
1078 {
1079     if (jScriptEnabled())
1080         [_bridge windowObjectCleared];
1081 }
1082
1083 void FrameMac::openURLFromPageCache(KWQPageState *state)
1084 {
1085     // It's safe to assume none of the KWQPageState methods will raise
1086     // exceptions, since KWQPageState is implemented by WebCore and
1087     // does not throw
1088
1089     Document *doc = [state document];
1090     Node *mousePressNode = [state mousePressNode];
1091     KURL *kurl = [state URL];
1092     SavedProperties *windowProperties = [state windowProperties];
1093     SavedProperties *locationProperties = [state locationProperties];
1094     SavedBuiltins *interpreterBuiltins = [state interpreterBuiltins];
1095     PausedTimeouts *timeouts = [state pausedTimeouts];
1096     
1097     cancelRedirection();
1098
1099     // We still have to close the previous part page.
1100     closeURL();
1101             
1102     d->m_bComplete = false;
1103     
1104     // Don't re-emit the load event.
1105     d->m_bLoadEventEmitted = true;
1106     
1107     // delete old status bar msg's from kjs (if it _was_ activated on last URL)
1108     if (jScriptEnabled()) {
1109         d->m_kjsStatusBarText = String();
1110         d->m_kjsDefaultStatusBarText = String();
1111     }
1112
1113     ASSERT(kurl);
1114     
1115     d->m_url = *kurl;
1116     
1117     // initializing m_url to the new url breaks relative links when opening such a link after this call and _before_ begin() is called (when the first
1118     // data arrives) (Simon)
1119     if (url().protocol().startsWith("http") && !url().host().isEmpty() && url().path().isEmpty())
1120         d->m_url.setPath("/");
1121     
1122     // copy to m_workingURL after fixing url() above
1123     d->m_workingURL = url();
1124         
1125     started();
1126     
1127     // -----------begin-----------
1128     clear();
1129
1130     doc->setInPageCache(NO);
1131
1132     d->m_bCleared = false;
1133     d->m_bComplete = false;
1134     d->m_bLoadEventEmitted = false;
1135     d->m_referrer = url().url();
1136     
1137     setView(doc->view());
1138     
1139     d->m_doc = doc;
1140     d->m_mousePressNode = mousePressNode;
1141     d->m_decoder = doc->decoder();
1142
1143     updatePolicyBaseURL();
1144
1145     { // scope the lock
1146         JSLock lock;
1147         restoreWindowProperties(windowProperties);
1148         restoreLocationProperties(locationProperties);
1149         restoreInterpreterBuiltins(*interpreterBuiltins);
1150     }
1151
1152     resumeTimeouts(timeouts);
1153     
1154     checkCompleted();
1155 }
1156
1157 WebCoreFrameBridge *FrameMac::bridgeForWidget(const Widget *widget)
1158 {
1159     ASSERT_ARG(widget, widget);
1160     
1161     FrameMac *frame = Mac(frameForWidget(widget));
1162     ASSERT(frame);
1163     return frame->bridge();
1164 }
1165
1166 NSView *FrameMac::documentViewForNode(Node *node)
1167 {
1168     WebCoreFrameBridge *bridge = Mac(frameForNode(node))->bridge();
1169     return [bridge documentView];
1170 }
1171
1172 void FrameMac::saveDocumentState()
1173 {
1174     // Do not save doc state if the page has a password field and a form that would be submitted
1175     // via https
1176     if (!(d->m_doc && d->m_doc->hasPasswordField() && d->m_doc->hasSecureForm())) {
1177         BEGIN_BLOCK_OBJC_EXCEPTIONS;
1178         [_bridge saveDocumentState];
1179         END_BLOCK_OBJC_EXCEPTIONS;
1180     }
1181 }
1182
1183 void FrameMac::restoreDocumentState()
1184 {
1185     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1186     [_bridge restoreDocumentState];
1187     END_BLOCK_OBJC_EXCEPTIONS;
1188 }
1189
1190 String FrameMac::incomingReferrer() const
1191 {
1192     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1193     return [_bridge incomingReferrer];
1194     END_BLOCK_OBJC_EXCEPTIONS;
1195
1196     return String();
1197 }
1198
1199 void FrameMac::runJavaScriptAlert(const String& message)
1200 {
1201     String text = message;
1202     text.replace(QChar('\\'), backslashAsCurrencySymbol());
1203     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1204     [_bridge runJavaScriptAlertPanelWithMessage:text];
1205     END_BLOCK_OBJC_EXCEPTIONS;
1206 }
1207
1208 bool FrameMac::runJavaScriptConfirm(const String& message)
1209 {
1210     String text = message;
1211     text.replace(QChar('\\'), backslashAsCurrencySymbol());
1212
1213     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1214     return [_bridge runJavaScriptConfirmPanelWithMessage:text];
1215     END_BLOCK_OBJC_EXCEPTIONS;
1216
1217     return false;
1218 }
1219
1220 bool FrameMac::runJavaScriptPrompt(const String& prompt, const String& defaultValue, String& result)
1221 {
1222     String promptText = prompt;
1223     promptText.replace(QChar('\\'), backslashAsCurrencySymbol());
1224     String defaultValueText = defaultValue;
1225     defaultValueText.replace(QChar('\\'), backslashAsCurrencySymbol());
1226
1227     bool ok;
1228     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1229     NSString *returnedText = nil;
1230
1231     ok = [_bridge runJavaScriptTextInputPanelWithPrompt:prompt
1232         defaultText:defaultValue returningText:&returnedText];
1233
1234     if (ok) {
1235         result = String(returnedText);
1236         result.replace(backslashAsCurrencySymbol(), QChar('\\'));
1237     }
1238
1239     return ok;
1240     END_BLOCK_OBJC_EXCEPTIONS;
1241     
1242     return false;
1243 }
1244
1245 bool FrameMac::locationbarVisible()
1246 {
1247     return [_bridge areToolbarsVisible];
1248 }
1249
1250 bool FrameMac::menubarVisible()
1251 {
1252     // The menubar is always on in Mac OS X UI
1253     return true;
1254 }
1255
1256 bool FrameMac::personalbarVisible()
1257 {
1258     return [_bridge areToolbarsVisible];
1259 }
1260
1261 bool FrameMac::statusbarVisible()
1262 {
1263     return [_bridge isStatusbarVisible];
1264 }
1265
1266 bool FrameMac::toolbarVisible()
1267 {
1268     return [_bridge areToolbarsVisible];
1269 }
1270
1271 void FrameMac::addMessageToConsole(const String &message, unsigned lineNumber, const String &sourceURL)
1272 {
1273     NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
1274         (NSString *)message, @"message",
1275         [NSNumber numberWithInt: lineNumber], @"lineNumber",
1276         (NSString *)sourceURL, @"sourceURL",
1277         NULL];
1278     [_bridge addMessageToConsole:dictionary];
1279 }
1280
1281 void FrameMac::createEmptyDocument()
1282 {
1283     // Although it's not completely clear from the name of this function,
1284     // it does nothing if we already have a document, and just creates an
1285     // empty one if we have no document at all.
1286     if (!d->m_doc) {
1287         BEGIN_BLOCK_OBJC_EXCEPTIONS;
1288         [_bridge loadEmptyDocumentSynchronously];
1289         END_BLOCK_OBJC_EXCEPTIONS;
1290
1291         updateBaseURLForEmptyDocument();
1292     }
1293 }
1294
1295 bool FrameMac::keyEvent(NSEvent *event)
1296 {
1297     bool result;
1298     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1299
1300     ASSERT([event type] == NSKeyDown || [event type] == NSKeyUp);
1301
1302     // Check for cases where we are too early for events -- possible unmatched key up
1303     // from pressing return in the location bar.
1304     Document *doc = document();
1305     if (!doc) {
1306         return false;
1307     }
1308     Node *node = doc->focusNode();
1309     if (!node) {
1310         if (doc->isHTMLDocument())
1311             node = doc->body();
1312         else
1313             node = doc->documentElement();
1314         if (!node)
1315             return false;
1316     }
1317     
1318     if ([event type] == NSKeyDown) {
1319         prepareForUserAction();
1320     }
1321
1322     NSEvent *oldCurrentEvent = _currentEvent;
1323     _currentEvent = KWQRetain(event);
1324
1325     PlatformKeyboardEvent qEvent(event);
1326     result = !EventTargetNodeCast(node)->dispatchKeyEvent(qEvent);
1327
1328     // We want to send both a down and a press for the initial key event.
1329     // To get KHTML to do this, we send a second KeyPress with "is repeat" set to true,
1330     // which causes it to send a press to the DOM.
1331     // That's not a great hack; it would be good to do this in a better way.
1332     if ([event type] == NSKeyDown && ![event isARepeat]) {
1333         PlatformKeyboardEvent repeatEvent(event, true);
1334         if (!EventTargetNodeCast(node)->dispatchKeyEvent(repeatEvent))
1335             result = true;
1336     }
1337
1338     ASSERT(_currentEvent == event);
1339     KWQRelease(event);
1340     _currentEvent = oldCurrentEvent;
1341
1342     return result;
1343
1344     END_BLOCK_OBJC_EXCEPTIONS;
1345
1346     return false;
1347 }
1348
1349 void FrameMac::handleMousePressEvent(const MouseEventWithHitTestResults& event)
1350 {
1351     bool singleClick = [_currentEvent clickCount] <= 1;
1352
1353     // If we got the event back, that must mean it wasn't prevented,
1354     // so it's allowed to start a drag or selection.
1355     _mouseDownMayStartSelect = canMouseDownStartSelect(event.innerNode());
1356     
1357     // Careful that the drag starting logic stays in sync with eventMayStartDrag()
1358     _mouseDownMayStartDrag = singleClick;
1359
1360     d->m_mousePressNode = event.innerNode();
1361     
1362     if (!passWidgetMouseDownEventToWidget(event, false)) {
1363         // We don't do this at the start of mouse down handling (before calling into WebCore),
1364         // because we don't want to do it until we know we didn't hit a widget.
1365         NSView *view = d->m_view->getDocumentView();
1366
1367         if (singleClick) {
1368             BEGIN_BLOCK_OBJC_EXCEPTIONS;
1369             if ([_bridge firstResponder] != view) {
1370                 [_bridge makeFirstResponder:view];
1371             }
1372             END_BLOCK_OBJC_EXCEPTIONS;
1373         }
1374
1375         Frame::handleMousePressEvent(event);
1376     }
1377 }
1378
1379 bool FrameMac::passMouseDownEventToWidget(Widget* widget)
1380 {
1381     // FIXME: this method always returns true
1382
1383     if (!widget) {
1384         LOG_ERROR("hit a RenderWidget without a corresponding Widget, means a frame is half-constructed");
1385         return true;
1386     }
1387
1388     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1389     
1390     NSView *nodeView = widget->getView();
1391     ASSERT(nodeView);
1392     ASSERT([nodeView superview]);
1393     NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[_currentEvent locationInWindow] fromView:nil]];
1394     if (view == nil) {
1395         LOG_ERROR("KHTML says we hit a RenderWidget, but AppKit doesn't agree we hit the corresponding NSView");
1396         return true;
1397     }
1398     
1399     if ([_bridge firstResponder] == view) {
1400         // In the case where we just became first responder, we should send the mouseDown:
1401         // to the NSTextField, not the NSTextField's editor. This code makes sure that happens.
1402         // If we don't do this, we see a flash of selected text when clicking in a text field.
1403         if (![_bridge wasFirstResponderAtMouseDownTime:view] && [view isKindOfClass:[NSTextView class]]) {
1404             NSView *superview = view;
1405             while (superview != nodeView) {
1406                 superview = [superview superview];
1407                 ASSERT(superview);
1408                 if ([superview isKindOfClass:[NSControl class]]) {
1409                     NSControl *control = static_cast<NSControl *>(superview);
1410                     if ([control currentEditor] == view) {
1411                         view = superview;
1412                     }
1413                     break;
1414                 }
1415             }
1416         }
1417     } else {
1418         // Normally [NSWindow sendEvent:] handles setting the first responder.
1419         // But in our case, the event was sent to the view representing the entire web page.
1420         if ([_currentEvent clickCount] <= 1 && [view acceptsFirstResponder] && [view needsPanelToBecomeKey]) {
1421             [_bridge makeFirstResponder:view];
1422         }
1423     }
1424
1425     // We need to "defer loading" and defer timers while we are tracking the mouse.
1426     // That's because we don't want the new page to load while the user is holding the mouse down.
1427     
1428     BOOL wasDeferringLoading = [_bridge defersLoading];
1429     if (!wasDeferringLoading)
1430         [_bridge setDefersLoading:YES];
1431     BOOL wasDeferringTimers = isDeferringTimers();
1432     if (!wasDeferringTimers)
1433         setDeferringTimers(true);
1434
1435     ASSERT(!_sendingEventToSubview);
1436     _sendingEventToSubview = true;
1437     [view mouseDown:_currentEvent];
1438     _sendingEventToSubview = false;
1439     
1440     if (!wasDeferringTimers)
1441         setDeferringTimers(false);
1442     if (!wasDeferringLoading)
1443         [_bridge setDefersLoading:NO];
1444
1445     // Remember which view we sent the event to, so we can direct the release event properly.
1446     _mouseDownView = view;
1447     _mouseDownWasInSubframe = false;
1448
1449     END_BLOCK_OBJC_EXCEPTIONS;
1450
1451     return true;
1452 }
1453
1454 bool FrameMac::lastEventIsMouseUp() const
1455 {
1456     // Many AK widgets run their own event loops and consume events while the mouse is down.
1457     // When they finish, currentEvent is the mouseUp that they exited on.  We need to update
1458     // the khtml state with this mouseUp, which khtml never saw.  This method lets us detect
1459     // that state.
1460
1461     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1462     NSEvent *currentEventAfterHandlingMouseDown = [NSApp currentEvent];
1463     if (_currentEvent != currentEventAfterHandlingMouseDown) {
1464         if ([currentEventAfterHandlingMouseDown type] == NSLeftMouseUp) {
1465             return true;
1466         }
1467     }
1468     END_BLOCK_OBJC_EXCEPTIONS;
1469
1470     return false;
1471 }
1472     
1473 // Note that this does the same kind of check as [target isDescendantOf:superview].
1474 // There are two differences: This is a lot slower because it has to walk the whole
1475 // tree, and this works in cases where the target has already been deallocated.
1476 static bool findViewInSubviews(NSView *superview, NSView *target)
1477 {
1478     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1479     NSEnumerator *e = [[superview subviews] objectEnumerator];
1480     NSView *subview;
1481     while ((subview = [e nextObject])) {
1482         if (subview == target || findViewInSubviews(subview, target)) {
1483             return true;
1484         }
1485     }
1486     END_BLOCK_OBJC_EXCEPTIONS;
1487     
1488     return false;
1489 }
1490
1491 NSView *FrameMac::mouseDownViewIfStillGood()
1492 {
1493     // Since we have no way of tracking the lifetime of _mouseDownView, we have to assume that
1494     // it could be deallocated already. We search for it in our subview tree; if we don't find
1495     // it, we set it to nil.
1496     NSView *mouseDownView = _mouseDownView;
1497     if (!mouseDownView) {
1498         return nil;
1499     }
1500     FrameView *topFrameView = d->m_view.get();
1501     NSView *topView = topFrameView ? topFrameView->getView() : nil;
1502     if (!topView || !findViewInSubviews(topView, mouseDownView)) {
1503         _mouseDownView = nil;
1504         return nil;
1505     }
1506     return mouseDownView;
1507 }
1508
1509 bool FrameMac::eventMayStartDrag(NSEvent *event) const
1510 {
1511     // This is a pre-flight check of whether the event might lead to a drag being started.  Be careful
1512     // that its logic needs to stay in sync with handleMouseMoveEvent() and the way we set
1513     // _mouseDownMayStartDrag in handleMousePressEvent
1514     
1515     if ([event type] != NSLeftMouseDown || [event clickCount] != 1) {
1516         return false;
1517     }
1518     
1519     BOOL DHTMLFlag, UAFlag;
1520     [_bridge allowDHTMLDrag:&DHTMLFlag UADrag:&UAFlag];
1521     if (!DHTMLFlag && !UAFlag) {
1522         return false;
1523     }
1524
1525     NSPoint loc = [event locationInWindow];
1526     IntPoint mouseDownPos = d->m_view->viewportToContents(IntPoint(loc));
1527     RenderObject::NodeInfo nodeInfo(true, false);
1528     renderer()->layer()->hitTest(nodeInfo, mouseDownPos);
1529     bool srcIsDHTML;
1530     return nodeInfo.innerNode()->renderer()->draggableNode(DHTMLFlag, UAFlag, mouseDownPos.x(), mouseDownPos.y(), srcIsDHTML);
1531 }
1532
1533 // The link drag hysteresis is much larger than the others because there
1534 // needs to be enough space to cancel the link press without starting a link drag,
1535 // and because dragging links is rare.
1536 const float LinkDragHysteresis = 40.0;
1537 const float ImageDragHysteresis = 5.0;
1538 const float TextDragHysteresis = 3.0;
1539 const float GeneralDragHysteresis = 3.0;
1540 const float TextDragDelay = 0.15;
1541
1542 bool FrameMac::dragHysteresisExceeded(float dragLocationX, float dragLocationY) const
1543 {
1544     IntPoint dragViewportLocation((int)dragLocationX, (int)dragLocationY);
1545     IntPoint dragLocation = d->m_view->viewportToContents(dragViewportLocation);
1546     IntSize delta = dragLocation - m_mouseDownPos;
1547     
1548     float threshold = GeneralDragHysteresis;
1549     if (_dragSrcIsImage)
1550         threshold = ImageDragHysteresis;
1551     else if (_dragSrcIsLink)
1552         threshold = LinkDragHysteresis;
1553     else if (_dragSrcInSelection)
1554         threshold = TextDragHysteresis;
1555
1556     return fabsf(delta.width()) >= threshold || fabsf(delta.height()) >= threshold;
1557 }
1558
1559 void FrameMac::handleMouseMoveEvent(const MouseEventWithHitTestResults& event)
1560 {
1561     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1562
1563     if ([_currentEvent type] == NSLeftMouseDragged) {
1564         NSView *view = mouseDownViewIfStillGood();
1565
1566         if (view) {
1567             _sendingEventToSubview = true;
1568             [view mouseDragged:_currentEvent];
1569             _sendingEventToSubview = false;
1570             return;
1571         }
1572
1573         // Careful that the drag starting logic stays in sync with eventMayStartDrag()
1574     
1575         if (_mouseDownMayStartDrag && !_dragSrc) {
1576             BOOL tempFlag1, tempFlag2;
1577             [_bridge allowDHTMLDrag:&tempFlag1 UADrag:&tempFlag2];
1578             _dragSrcMayBeDHTML = tempFlag1;
1579             _dragSrcMayBeUA = tempFlag2;
1580             if (!_dragSrcMayBeDHTML && !_dragSrcMayBeUA) {
1581                 _mouseDownMayStartDrag = false;     // no element is draggable
1582             }
1583         }
1584         
1585         if (_mouseDownMayStartDrag && !_dragSrc) {
1586             // try to find an element that wants to be dragged
1587             RenderObject::NodeInfo nodeInfo(true, false);
1588             renderer()->layer()->hitTest(nodeInfo, m_mouseDownPos);
1589             Node *node = nodeInfo.innerNode();
1590             _dragSrc = (node && node->renderer()) ? node->renderer()->draggableNode(_dragSrcMayBeDHTML, _dragSrcMayBeUA, m_mouseDownPos.x(), m_mouseDownPos.y(), _dragSrcIsDHTML) : 0;
1591             if (!_dragSrc) {
1592                 _mouseDownMayStartDrag = false;     // no element is draggable
1593             } else {
1594                 // remember some facts about this source, while we have a NodeInfo handy
1595                 node = nodeInfo.URLElement();
1596                 _dragSrcIsLink = node && node->isLink();
1597
1598                 node = nodeInfo.innerNonSharedNode();
1599                 _dragSrcIsImage = node && node->renderer() && node->renderer()->isImage();
1600                 
1601                 _dragSrcInSelection = isPointInsideSelection(m_mouseDownPos);
1602             }                
1603         }
1604         
1605         // For drags starting in the selection, the user must wait between the mousedown and mousedrag,
1606         // or else we bail on the dragging stuff and allow selection to occur
1607         if (_mouseDownMayStartDrag && _dragSrcInSelection && [_currentEvent timestamp] - _mouseDownTimestamp < TextDragDelay) {
1608             _mouseDownMayStartDrag = false;
1609             // ...but if this was the first click in the window, we don't even want to start selection
1610             if (_activationEventNumber == [_currentEvent eventNumber]) {
1611                 _mouseDownMayStartSelect = false;
1612             }
1613         }
1614
1615         if (_mouseDownMayStartDrag) {
1616             // We are starting a text/image/url drag, so the cursor should be an arrow
1617             d->m_view->setCursor(pointerCursor());
1618             
1619             NSPoint dragLocation = [_currentEvent locationInWindow];
1620             if (dragHysteresisExceeded(dragLocation.x, dragLocation.y)) {
1621                 
1622                 // Once we're past the hysteresis point, we don't want to treat this gesture as a click
1623                 d->m_view->invalidateClick();
1624
1625                 NSImage *dragImage = nil;       // we use these values if WC is out of the loop
1626                 NSPoint dragLoc = NSZeroPoint;
1627                 NSDragOperation srcOp = NSDragOperationNone;                
1628                 BOOL wcWrotePasteboard = NO;
1629                 if (_dragSrcMayBeDHTML) {
1630                     NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
1631                     // Must be done before ondragstart adds types and data to the pboard,
1632                     // also done for security, as it erases data from the last drag
1633                     [pasteboard declareTypes:[NSArray array] owner:nil];
1634                     
1635                     freeClipboard();    // would only happen if we missed a dragEnd.  Do it anyway, just
1636                                         // to make sure it gets numbified
1637                     _dragClipboard = new ClipboardMac(true, pasteboard, ClipboardMac::Writable, this);
1638                     
1639                     // If this is drag of an element, get set up to generate a default image.  Otherwise
1640                     // WebKit will generate the default, the element doesn't override.
1641                     if (_dragSrcIsDHTML) {
1642                         int srcX, srcY;
1643                         _dragSrc->renderer()->absolutePosition(srcX, srcY);
1644                         IntSize delta = m_mouseDownPos - IntPoint(srcX, srcY);
1645                         _dragClipboard->setDragImageElement(_dragSrc.get(), IntPoint() + delta);
1646                     } 
1647
1648                     _mouseDownMayStartDrag = dispatchDragSrcEvent(dragstartEvent, m_mouseDown);
1649                     // Invalidate clipboard here against anymore pasteboard writing for security.  The drag
1650                     // image can still be changed as we drag, but not the pasteboard data.
1651                     _dragClipboard->setAccessPolicy(ClipboardMac::ImageWritable);
1652                     
1653                     if (_mouseDownMayStartDrag) {
1654                         // gather values from DHTML element, if it set any
1655                         _dragClipboard->sourceOperation(&srcOp);
1656
1657                         NSArray *types = [pasteboard types];
1658                         wcWrotePasteboard = types && [types count] > 0;
1659
1660                         if (_dragSrcMayBeDHTML) {
1661                             dragImage = _dragClipboard->dragNSImage(&dragLoc);
1662                         }
1663                         
1664                         // Yuck, dragSourceMovedTo() can be called as a result of kicking off the drag with
1665                         // dragImage!  Because of that dumb reentrancy, we may think we've not started the
1666                         // drag when that happens.  So we have to assume it's started before we kick it off.
1667                         _dragClipboard->setDragHasStarted();
1668                     }
1669                 }
1670                 
1671                 if (_mouseDownMayStartDrag) {
1672                     BOOL startedDrag = [_bridge startDraggingImage:dragImage at:dragLoc operation:srcOp event:_currentEvent sourceIsDHTML:_dragSrcIsDHTML DHTMLWroteData:wcWrotePasteboard];
1673                     if (!startedDrag && _dragSrcMayBeDHTML) {
1674                         // WebKit canned the drag at the last minute - we owe _dragSrc a DRAGEND event
1675                         PlatformMouseEvent event;
1676                         dispatchDragSrcEvent(dragendEvent, event);
1677                         _mouseDownMayStartDrag = false;
1678                     }
1679                 } 
1680
1681                 if (!_mouseDownMayStartDrag) {
1682                     // something failed to start the drag, cleanup
1683                     freeClipboard();
1684                     _dragSrc = 0;
1685                 }
1686             }
1687
1688             // No more default handling (like selection), whether we're past the hysteresis bounds or not
1689             return;
1690         }
1691         if (!_mouseDownMayStartSelect) {
1692             return;
1693         }
1694
1695         // Don't allow dragging or click handling after we've started selecting.
1696         _mouseDownMayStartDrag = false;
1697         d->m_view->invalidateClick();
1698
1699         Node* node = event.innerNode();
1700         RenderLayer* layer = 0;
1701         if (node && node->renderer())
1702             layer = node->renderer()->enclosingLayer();
1703             
1704         // If the selection is contained in a layer that can scroll, that layer should handle the autoscroll
1705         // Otherwise, let the bridge handle it so the view can scroll itself.
1706         while (layer && !layer->shouldAutoscroll())
1707             layer = layer->parent();
1708         if (layer)
1709             handleAutoscroll(layer);
1710         else
1711             [_bridge handleAutoscrollForMouseDragged:_currentEvent];
1712             
1713     } else {
1714         // If we allowed the other side of the bridge to handle a drag
1715         // last time, then m_bMousePressed might still be set. So we
1716         // clear it now to make sure the next move after a drag
1717         // doesn't look like a drag.
1718         d->m_bMousePressed = false;
1719     }
1720
1721     Frame::handleMouseMoveEvent(event);
1722
1723     END_BLOCK_OBJC_EXCEPTIONS;
1724 }
1725
1726 // Returns whether caller should continue with "the default processing", which is the same as 
1727 // the event handler NOT setting the return value to false
1728 bool FrameMac::dispatchCPPEvent(const AtomicString &eventType, ClipboardMac::AccessPolicy policy)
1729 {
1730     Node* target = d->m_selection.start().element();
1731     if (!target && document())
1732         target = document()->body();
1733     if (!target)
1734         return true;
1735
1736     RefPtr<ClipboardMac> clipboard = new ClipboardMac(false, [NSPasteboard generalPasteboard], (ClipboardMac::AccessPolicy)policy);
1737
1738     ExceptionCode ec = 0;
1739     RefPtr<Event> evt = new ClipboardEvent(eventType, true, true, clipboard.get());
1740     EventTargetNodeCast(target)->dispatchEvent(evt, ec, true);
1741     bool noDefaultProcessing = evt->defaultPrevented();
1742
1743     // invalidate clipboard here for security
1744     clipboard->setAccessPolicy(ClipboardMac::Numb);
1745
1746     return !noDefaultProcessing;
1747 }
1748
1749 // WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items.  They
1750 // also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items.
1751 // We need to use onbeforecopy as a real menu enabler because we allow elements that are not
1752 // normally selectable to implement copy/paste (like divs, or a document body).
1753
1754 bool FrameMac::mayCut()
1755 {
1756     return !dispatchCPPEvent(beforecutEvent, ClipboardMac::Numb);
1757 }
1758
1759 bool FrameMac::mayCopy()
1760 {
1761     return !dispatchCPPEvent(beforecopyEvent, ClipboardMac::Numb);
1762 }
1763
1764 bool FrameMac::mayPaste()
1765 {
1766     return !dispatchCPPEvent(beforepasteEvent, ClipboardMac::Numb);
1767 }
1768
1769 bool FrameMac::tryCut()
1770 {
1771     // Must be done before oncut adds types and data to the pboard,
1772     // also done for security, as it erases data from the last copy/paste.
1773     [[NSPasteboard generalPasteboard] declareTypes:[NSArray array] owner:nil];
1774
1775     return !dispatchCPPEvent(cutEvent, ClipboardMac::Writable);
1776 }
1777
1778 bool FrameMac::tryCopy()
1779 {
1780     // Must be done before oncopy adds types and data to the pboard,
1781     // also done for security, as it erases data from the last copy/paste.
1782     [[NSPasteboard generalPasteboard] declareTypes:[NSArray array] owner:nil];
1783
1784     return !dispatchCPPEvent(copyEvent, ClipboardMac::Writable);
1785 }
1786
1787 bool FrameMac::tryPaste()
1788 {
1789     return !dispatchCPPEvent(pasteEvent, ClipboardMac::Readable);
1790 }
1791
1792 void FrameMac::handleMouseReleaseEvent(const MouseEventWithHitTestResults& event)
1793 {
1794     NSView *view = mouseDownViewIfStillGood();
1795     if (!view) {
1796         // If this was the first click in the window, we don't even want to clear the selection.
1797         // This case occurs when the user clicks on a draggable element, since we have to process
1798         // the mouse down and drag events to see if we might start a drag.  For other first clicks
1799         // in a window, we just don't acceptFirstMouse, and the whole down-drag-up sequence gets
1800         // ignored upstream of this layer.
1801         if (_activationEventNumber != [_currentEvent eventNumber])
1802             Frame::handleMouseReleaseEvent(event);
1803         return;
1804     }
1805     stopAutoscrollTimer();
1806     
1807     _sendingEventToSubview = true;
1808     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1809     [view mouseUp:_currentEvent];
1810     END_BLOCK_OBJC_EXCEPTIONS;
1811     _sendingEventToSubview = false;
1812 }
1813
1814 bool FrameMac::passSubframeEventToSubframe(MouseEventWithHitTestResults& event, Frame* subframePart)
1815 {
1816     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1817
1818     switch ([_currentEvent type]) {
1819         case NSMouseMoved: {
1820             ASSERT(subframePart);
1821             [Mac(subframePart)->bridge() mouseMoved:_currentEvent];
1822             return true;
1823         }
1824         
1825         case NSLeftMouseDown: {
1826             Node *node = event.innerNode();
1827             if (!node) {
1828                 return false;
1829             }
1830             RenderObject *renderer = node->renderer();
1831             if (!renderer || !renderer->isWidget()) {
1832                 return false;
1833             }
1834             Widget *widget = static_cast<RenderWidget *>(renderer)->widget();
1835             if (!widget || !widget->isFrameView())
1836                 return false;
1837             if (!passWidgetMouseDownEventToWidget(static_cast<RenderWidget *>(renderer))) {
1838                 return false;
1839             }
1840             _mouseDownWasInSubframe = true;
1841             return true;
1842         }
1843         case NSLeftMouseUp: {
1844             if (!_mouseDownWasInSubframe) {
1845                 return false;
1846             }
1847             NSView *view = mouseDownViewIfStillGood();
1848             if (!view) {
1849                 return false;
1850             }
1851             ASSERT(!_sendingEventToSubview);
1852             _sendingEventToSubview = true;
1853             [view mouseUp:_currentEvent];
1854             _sendingEventToSubview = false;
1855             return true;
1856         }
1857         case NSLeftMouseDragged: {
1858             if (!_mouseDownWasInSubframe) {
1859                 return false;
1860             }
1861             NSView *view = mouseDownViewIfStillGood();
1862             if (!view) {
1863                 return false;
1864             }
1865             ASSERT(!_sendingEventToSubview);
1866             _sendingEventToSubview = true;
1867             [view mouseDragged:_currentEvent];
1868             _sendingEventToSubview = false;
1869             return true;
1870         }
1871         default:
1872             return false;
1873     }
1874     END_BLOCK_OBJC_EXCEPTIONS;
1875
1876     return false;
1877 }
1878
1879 bool FrameMac::passWheelEventToChildWidget(Node *node)
1880 {
1881     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1882         
1883     if ([_currentEvent type] != NSScrollWheel || _sendingEventToSubview || !node) 
1884         return false;
1885     else {
1886         RenderObject *renderer = node->renderer();
1887         if (!renderer || !renderer->isWidget())
1888             return false;
1889         Widget *widget = static_cast<RenderWidget *>(renderer)->widget();
1890         if (!widget)
1891             return false;
1892             
1893         NSView *nodeView = widget->getView();
1894         ASSERT(nodeView);
1895         ASSERT([nodeView superview]);
1896         NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[_currentEvent locationInWindow] fromView:nil]];
1897     
1898         ASSERT(view);
1899         _sendingEventToSubview = true;
1900         [view scrollWheel:_currentEvent];
1901         _sendingEventToSubview = false;
1902         return true;
1903     }
1904             
1905     END_BLOCK_OBJC_EXCEPTIONS;
1906     return false;
1907 }
1908
1909 void FrameMac::mouseDown(NSEvent *event)
1910 {
1911     FrameView *v = d->m_view.get();
1912     if (!v || _sendingEventToSubview)
1913         return;
1914
1915     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1916
1917     prepareForUserAction();
1918
1919     _mouseDownView = nil;
1920     _dragSrc = 0;
1921     
1922     NSEvent *oldCurrentEvent = _currentEvent;
1923     _currentEvent = KWQRetain(event);
1924     m_mouseDown = PlatformMouseEvent(event);
1925     NSPoint loc = [event locationInWindow];
1926     m_mouseDownPos = d->m_view->viewportToContents(IntPoint(loc));
1927     _mouseDownTimestamp = [event timestamp];
1928
1929     _mouseDownMayStartDrag = false;
1930     _mouseDownMayStartSelect = false;
1931
1932     v->handleMousePressEvent(event);
1933     
1934     ASSERT(_currentEvent == event);
1935     KWQRelease(event);
1936     _currentEvent = oldCurrentEvent;
1937
1938     END_BLOCK_OBJC_EXCEPTIONS;
1939 }
1940
1941 void FrameMac::mouseDragged(NSEvent *event)
1942 {
1943     FrameView *v = d->m_view.get();
1944     if (!v || _sendingEventToSubview) {
1945         return;
1946     }
1947
1948     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1949
1950     NSEvent *oldCurrentEvent = _currentEvent;
1951     _currentEvent = KWQRetain(event);
1952
1953     v->handleMouseMoveEvent(event);
1954     
1955     ASSERT(_currentEvent == event);
1956     KWQRelease(event);
1957     _currentEvent = oldCurrentEvent;
1958
1959     END_BLOCK_OBJC_EXCEPTIONS;
1960 }
1961
1962 void FrameMac::mouseUp(NSEvent *event)
1963 {
1964     FrameView *v = d->m_view.get();
1965     if (!v || _sendingEventToSubview)
1966         return;
1967
1968     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1969
1970     NSEvent *oldCurrentEvent = _currentEvent;
1971     _currentEvent = KWQRetain(event);
1972
1973     // Our behavior here is a little different that Qt. Qt always sends
1974     // a mouse release event, even for a double click. To correct problems
1975     // in khtml's DOM click event handling we do not send a release here
1976     // for a double click. Instead we send that event from FrameView's
1977     // handleMouseDoubleClickEvent. Note also that the third click of
1978     // a triple click is treated as a single click, but the fourth is then
1979     // treated as another double click. Hence the "% 2" below.
1980     int clickCount = [event clickCount];
1981     if (clickCount > 0 && clickCount % 2 == 0)
1982         v->handleMouseDoubleClickEvent(event);
1983     else
1984         v->handleMouseReleaseEvent(event);
1985     
1986     ASSERT(_currentEvent == event);
1987     KWQRelease(event);
1988     _currentEvent = oldCurrentEvent;
1989     
1990     _mouseDownView = nil;
1991
1992     END_BLOCK_OBJC_EXCEPTIONS;
1993 }
1994
1995 /*
1996  A hack for the benefit of AK's PopUpButton, which uses the Carbon menu manager, which thus
1997  eats all subsequent events after it is starts its modal tracking loop.  After the interaction
1998  is done, this routine is used to fix things up.  When a mouse down started us tracking in
1999  the widget, we post a fake mouse up to balance the mouse down we started with. When a 
2000  key down started us tracking in the widget, we post a fake key up to balance things out.
2001  In addition, we post a fake mouseMoved to get the cursor in sync with whatever we happen to 
2002  be over after the tracking is done.
2003  */
2004 void FrameMac::sendFakeEventsAfterWidgetTracking(NSEvent *initiatingEvent)
2005 {
2006     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2007
2008     _sendingEventToSubview = false;
2009     int eventType = [initiatingEvent type];
2010     if (eventType == NSLeftMouseDown || eventType == NSKeyDown) {
2011         NSEvent *fakeEvent = nil;
2012         if (eventType == NSLeftMouseDown) {
2013             fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
2014                                     location:[initiatingEvent locationInWindow]
2015                                 modifierFlags:[initiatingEvent modifierFlags]
2016                                     timestamp:[initiatingEvent timestamp]
2017                                 windowNumber:[initiatingEvent windowNumber]
2018                                         context:[initiatingEvent context]
2019                                     eventNumber:[initiatingEvent eventNumber]
2020                                     clickCount:[initiatingEvent clickCount]
2021                                     pressure:[initiatingEvent pressure]];
2022         
2023             mouseUp(fakeEvent);
2024         }
2025         else { // eventType == NSKeyDown
2026             fakeEvent = [NSEvent keyEventWithType:NSKeyUp
2027                                     location:[initiatingEvent locationInWindow]
2028                                modifierFlags:[initiatingEvent modifierFlags]
2029                                    timestamp:[initiatingEvent timestamp]
2030                                 windowNumber:[initiatingEvent windowNumber]
2031                                      context:[initiatingEvent context]
2032                                   characters:[initiatingEvent characters] 
2033                  charactersIgnoringModifiers:[initiatingEvent charactersIgnoringModifiers] 
2034                                    isARepeat:[initiatingEvent isARepeat] 
2035                                      keyCode:[initiatingEvent keyCode]];
2036             keyEvent(fakeEvent);
2037         }
2038         // FIXME:  We should really get the current modifierFlags here, but there's no way to poll
2039         // them in Cocoa, and because the event stream was stolen by the Carbon menu code we have
2040         // no up-to-date cache of them anywhere.
2041         fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
2042                                        location:[[_bridge window] convertScreenToBase:[NSEvent mouseLocation]]
2043                                   modifierFlags:[initiatingEvent modifierFlags]
2044                                       timestamp:[initiatingEvent timestamp]
2045                                    windowNumber:[initiatingEvent windowNumber]
2046                                         context:[initiatingEvent context]
2047                                     eventNumber:0
2048                                      clickCount:0
2049                                        pressure:0];
2050         mouseMoved(fakeEvent);
2051     }
2052     
2053     END_BLOCK_OBJC_EXCEPTIONS;
2054 }
2055
2056 void FrameMac::mouseMoved(NSEvent *event)
2057 {
2058     FrameView *v = d->m_view.get();
2059     // Reject a mouse moved if the button is down - screws up tracking during autoscroll
2060     // These happen because WebKit sometimes has to fake up moved events.
2061     if (!v || d->m_bMousePressed || _sendingEventToSubview)
2062         return;
2063     
2064     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2065
2066     NSEvent *oldCurrentEvent = _currentEvent;
2067     _currentEvent = KWQRetain(event);
2068     
2069     v->handleMouseMoveEvent(event);
2070     
2071     ASSERT(_currentEvent == event);
2072     KWQRelease(event);
2073     _currentEvent = oldCurrentEvent;
2074
2075     END_BLOCK_OBJC_EXCEPTIONS;
2076 }
2077
2078 // Called as we walk up the element chain for nodes with CSS property -khtml-user-drag == auto
2079 bool FrameMac::shouldDragAutoNode(Node* node, const IntPoint& point) const
2080 {
2081     // We assume that WebKit only cares about dragging things that can be leaf nodes (text, images, urls).
2082     // This saves a bunch of expensive calls (creating WC and WK element dicts) as we walk farther up
2083     // the node hierarchy, and we also don't have to cook up a way to ask WK about non-leaf nodes
2084     // (since right now WK just hit-tests using a cached lastMouseDown).
2085     if (!node->hasChildNodes() && d->m_view) {
2086         NSPoint eventLoc = d->m_view->contentsToViewport(point);
2087         return [_bridge mayStartDragAtEventLocation:eventLoc];
2088     } else
2089         return NO;
2090 }
2091
2092 bool FrameMac::sendContextMenuEvent(NSEvent *event)
2093 {
2094     Document* doc = d->m_doc.get();
2095     FrameView* v = d->m_view.get();
2096     if (!doc || !v)
2097         return false;
2098
2099     bool swallowEvent;
2100     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2101
2102     NSEvent *oldCurrentEvent = _currentEvent;
2103     _currentEvent = KWQRetain(event);
2104     
2105     PlatformMouseEvent mouseEvent(event);
2106
2107     IntPoint viewportPos = v->viewportToContents(mouseEvent.pos());
2108     MouseEventWithHitTestResults mev = doc->prepareMouseEvent(false, true, false, viewportPos, mouseEvent);
2109
2110     swallowEvent = v->dispatchMouseEvent(contextmenuEvent, mev.innerNode(), true, 0, mouseEvent, true);
2111     if (!swallowEvent && !isPointInsideSelection(viewportPos) &&
2112         ([_bridge selectWordBeforeMenuEvent] || [_bridge isEditable] || mev.innerNode()->isContentEditable())) {
2113         _mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection
2114         selectClosestWordFromMouseEvent(mouseEvent, mev.innerNode());
2115     }
2116
2117     ASSERT(_currentEvent == event);
2118     KWQRelease(event);
2119     _currentEvent = oldCurrentEvent;
2120
2121     return swallowEvent;
2122
2123     END_BLOCK_OBJC_EXCEPTIONS;
2124
2125     return false;
2126 }
2127
2128 struct ListItemInfo {
2129     unsigned start;
2130     unsigned end;
2131 };
2132
2133 NSFileWrapper *FrameMac::fileWrapperForElement(Element *e)
2134 {
2135     NSFileWrapper *wrapper = nil;
2136     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2137     
2138     const AtomicString& attr = e->getAttribute(srcAttr);
2139     if (!attr.isEmpty()) {
2140         NSURL *URL = completeURL(attr.deprecatedString()).getNSURL();
2141         wrapper = [_bridge fileWrapperForURL:URL];
2142     }    
2143     if (!wrapper) {
2144         RenderImage *renderer = static_cast<RenderImage *>(e->renderer());
2145         if (renderer->cachedImage() && !renderer->cachedImage()->isErrorImage()) {
2146             wrapper = [[NSFileWrapper alloc] initRegularFileWithContents:(NSData*)(renderer->cachedImage()->image()->getTIFFRepresentation())];
2147             [wrapper setPreferredFilename:@"image.tiff"];
2148             [wrapper autorelease];
2149         }
2150     }
2151
2152     return wrapper;
2153
2154     END_BLOCK_OBJC_EXCEPTIONS;
2155
2156     return nil;
2157 }
2158
2159 static Element *listParent(Element *item)
2160 {
2161     while (!item->hasTagName(ulTag) && !item->hasTagName(olTag)) {
2162         item = static_cast<Element *>(item->parentNode());
2163         if (!item)
2164             break;
2165     }
2166     return item;
2167 }
2168
2169 static Node* isTextFirstInListItem(Node *e)
2170 {
2171     if (!e->isTextNode())
2172         return 0;
2173     Node* par = e->parentNode();
2174     while (par) {
2175         if (par->firstChild() != e)
2176             return 0;
2177         if (par->hasTagName(liTag))
2178             return par;
2179         e = par;
2180         par = par->parentNode();
2181     }
2182     return 0;
2183 }
2184
2185 // FIXME: This collosal function needs to be refactored into maintainable smaller bits.
2186
2187 #define BULLET_CHAR 0x2022
2188 #define SQUARE_CHAR 0x25AA
2189 #define CIRCLE_CHAR 0x25E6
2190
2191 NSAttributedString *FrameMac::attributedString(Node *_start, int startOffset, Node *endNode, int endOffset)
2192 {
2193     ListItemInfo info;
2194     NSMutableAttributedString *result;
2195     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2196
2197     Node * _startNode = _start;
2198
2199     if (!_startNode)
2200         return nil;
2201
2202     result = [[[NSMutableAttributedString alloc] init] autorelease];
2203
2204     bool hasNewLine = true;
2205     bool addedSpace = true;
2206     NSAttributedString *pendingStyledSpace = nil;
2207     bool hasParagraphBreak = true;
2208     const Element *linkStartNode = 0;
2209     unsigned linkStartLocation = 0;
2210     DeprecatedPtrList<Element> listItems;
2211     DeprecatedValueList<ListItemInfo> listItemLocations;
2212     float maxMarkerWidth = 0;
2213     
2214     Node *n = _startNode;
2215     
2216     // If the first item is the entire text of a list item, use the list item node as the start of the 
2217     // selection, not the text node.  The user's intent was probably to select the list.
2218     if (n->isTextNode() && startOffset == 0) {
2219         Node *startListNode = isTextFirstInListItem(_startNode);
2220         if (startListNode){
2221             _startNode = startListNode;
2222             n = _startNode;
2223         }
2224     }
2225     
2226     while (n) {
2227         RenderObject *renderer = n->renderer();
2228         if (renderer) {
2229             RenderStyle *style = renderer->style();
2230             NSFont *font = style->font().getNSFont();
2231             bool needSpace = pendingStyledSpace != nil;
2232             if (n->isTextNode()) {
2233                 if (hasNewLine) {
2234                     addedSpace = true;
2235                     needSpace = false;
2236                     [pendingStyledSpace release];
2237                     pendingStyledSpace = nil;
2238                     hasNewLine = false;
2239                 }
2240                 DeprecatedString text;
2241                 DeprecatedString str = n->nodeValue().deprecatedString();
2242                 int start = (n == _startNode) ? startOffset : -1;
2243                 int end = (n == endNode) ? endOffset : -1;
2244                 if (renderer->isText()) {
2245                     if (!style->collapseWhiteSpace()) {
2246                         if (needSpace && !addedSpace) {
2247                             if (text.isEmpty() && linkStartLocation == [result length])
2248                                 ++linkStartLocation;
2249                             [result appendAttributedString:pendingStyledSpace];
2250                         }
2251                         int runStart = (start == -1) ? 0 : start;
2252                         int runEnd = (end == -1) ? str.length() : end;
2253                         text += str.mid(runStart, runEnd-runStart);
2254                         [pendingStyledSpace release];
2255                         pendingStyledSpace = nil;
2256                         addedSpace = str[runEnd-1].direction() == QChar::DirWS;
2257                     }
2258                     else {
2259                         RenderText* textObj = static_cast<RenderText*>(renderer);
2260                         if (!textObj->firstTextBox() && str.length() > 0 && !addedSpace) {
2261                             // We have no runs, but we do have a length.  This means we must be
2262                             // whitespace that collapsed away at the end of a line.
2263                             text += ' ';
2264                             addedSpace = true;
2265                         }
2266                         else {
2267                             addedSpace = false;
2268                             for (InlineTextBox* box = textObj->firstTextBox(); box; box = box->nextTextBox()) {
2269                                 int runStart = (start == -1) ? box->m_start : start;
2270                                 int runEnd = (end == -1) ? box->m_start + box->m_len : end;
2271                                 runEnd = kMin(runEnd, box->m_start + box->m_len);
2272                                 if (runStart >= box->m_start &&
2273                                     runStart < box->m_start + box->m_len) {
2274                                     if (box == textObj->firstTextBox() && box->m_start == runStart && runStart > 0)
2275                                         needSpace = true; // collapsed space at the start
2276                                     if (needSpace && !addedSpace) {
2277                                         if (pendingStyledSpace != nil) {
2278                                             if (text.isEmpty() && linkStartLocation == [result length])
2279                                                 ++linkStartLocation;
2280                                             [result appendAttributedString:pendingStyledSpace];
2281                                         } else
2282                                             text += ' ';
2283                                     }
2284                                     DeprecatedString runText = str.mid(runStart, runEnd - runStart);
2285                                     runText.replace('\n', ' ');
2286                                     text += runText;
2287                                     int nextRunStart = box->nextTextBox() ? box->nextTextBox()->m_start : str.length(); // collapsed space between runs or at the end
2288                                     needSpace = nextRunStart > runEnd;
2289                                     [pendingStyledSpace release];
2290                                     pendingStyledSpace = nil;
2291                                     addedSpace = str[runEnd-1].direction() == QChar::DirWS;
2292                                     start = -1;
2293                                 }
2294                                 if (end != -1 && runEnd >= end)
2295                                     break;
2296                             }
2297                         }
2298                     }
2299                 }
2300                 
2301                 text.replace(QChar('\\'), renderer->backslashAsCurrencySymbol());
2302     
2303                 if (text.length() > 0 || needSpace) {
2304                     NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init];
2305                     [attrs setObject:font forKey:NSFontAttributeName];
2306                     if (style && style->color().isValid() && style->color().alpha() != 0)
2307                         [attrs setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2308                     if (style && style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0)
2309                         [attrs setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2310
2311                     if (text.length() > 0) {
2312                         hasParagraphBreak = false;
2313                         NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString() attributes:attrs];
2314                         [result appendAttributedString: partialString];                
2315                         [partialString release];
2316                     }
2317
2318                     if (needSpace) {
2319                         [pendingStyledSpace release];
2320                         pendingStyledSpace = [[NSAttributedString alloc] initWithString:@" " attributes:attrs];
2321                     }
2322
2323                     [attrs release];
2324                 }
2325             } else {
2326                 // This is our simple HTML -> ASCII transformation:
2327                 DeprecatedString text;
2328                 if (n->hasTagName(aTag)) {
2329                     // Note the start of the <a> element.  We will add the NSLinkAttributeName
2330                     // attribute to the attributed string when navigating to the next sibling 
2331                     // of this node.
2332                     linkStartLocation = [result length];
2333                     linkStartNode = static_cast<Element*>(n);
2334                 } else if (n->hasTagName(brTag)) {
2335                     text += "\n";
2336                     hasNewLine = true;
2337                 } else if (n->hasTagName(liTag)) {
2338                     DeprecatedString listText;
2339                     Element *itemParent = listParent(static_cast<Element *>(n));
2340                     
2341                     if (!hasNewLine)
2342                         listText += '\n';
2343                     hasNewLine = true;
2344
2345                     listItems.append(static_cast<Element*>(n));
2346                     info.start = [result length];
2347                     info.end = 0;
2348                     listItemLocations.append (info);
2349                     
2350                     listText += '\t';
2351                     if (itemParent && renderer->isListItem()) {
2352                         RenderListItem *listRenderer = static_cast<RenderListItem*>(renderer);
2353
2354                         maxMarkerWidth = MAX([font pointSize], maxMarkerWidth);
2355                         switch(style->listStyleType()) {
2356                             case DISC:
2357                                 listText += ((QChar)BULLET_CHAR);
2358                                 break;
2359                             case CIRCLE:
2360                                 listText += ((QChar)CIRCLE_CHAR);
2361                                 break;
2362                             case SQUARE:
2363                                 listText += ((QChar)SQUARE_CHAR);
2364                                 break;
2365                             case LNONE:
2366                                 break;
2367                             default:
2368                                 DeprecatedString marker = listRenderer->markerStringValue();
2369                                 listText += marker;
2370                                 // Use AppKit metrics.  Will be rendered by AppKit.
2371                                 float markerWidth = [marker.getNSString() sizeWithAttributes:[NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]].width;
2372                                 maxMarkerWidth = MAX(markerWidth, maxMarkerWidth);
2373                         }
2374
2375                         listText += ' ';
2376                         listText += '\t';
2377
2378                         NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init];
2379                         [attrs setObject:font forKey:NSFontAttributeName];
2380                         if (style && style->color().isValid())
2381                             [attrs setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2382                         if (style && style->backgroundColor().isValid())
2383                             [attrs setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2384
2385                         NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:listText.getNSString() attributes:attrs];
2386                         [attrs release];
2387                         [result appendAttributedString: partialString];                
2388                         [partialString release];
2389                     }
2390                 } else if (n->hasTagName(olTag) || n->hasTagName(ulTag)) {
2391                     if (!hasNewLine)
2392                         text += "\n";
2393                     hasNewLine = true;
2394                 } else if (n->hasTagName(blockquoteTag)
2395                         || n->hasTagName(ddTag)
2396                         || n->hasTagName(divTag)
2397                         || n->hasTagName(dlTag)
2398                         || n->hasTagName(dtTag)
2399                         || n->hasTagName(hrTag)
2400                         || n->hasTagName(listingTag)
2401                         || n->hasTagName(preTag)
2402                         || n->hasTagName(tdTag)
2403                         || n->hasTagName(thTag)) {
2404                     if (!hasNewLine)
2405                         text += '\n';
2406                     hasNewLine = true;
2407                 } else if (n->hasTagName(h1Tag)
2408                         || n->hasTagName(h2Tag)
2409                         || n->hasTagName(h3Tag)
2410                         || n->hasTagName(h4Tag)
2411                         || n->hasTagName(h5Tag)
2412                         || n->hasTagName(h6Tag)
2413                         || n->hasTagName(pTag)
2414                         || n->hasTagName(trTag)) {
2415                     if (!hasNewLine)
2416                         text += '\n';
2417                     
2418                     // In certain cases, emit a paragraph break.
2419                     int bottomMargin = renderer->collapsedMarginBottom();
2420                     int fontSize = style->fontDescription().computedPixelSize();
2421                     if (bottomMargin * 2 >= fontSize) {
2422                         if (!hasParagraphBreak) {
2423                             text += '\n';
2424                             hasParagraphBreak = true;
2425                         }
2426                     }
2427                     
2428                     hasNewLine = true;
2429                 }
2430                 else if (n->hasTagName(imgTag)) {
2431                     if (pendingStyledSpace != nil) {
2432                         if (linkStartLocation == [result length])
2433                             ++linkStartLocation;
2434                         [result appendAttributedString:pendingStyledSpace];
2435                         [pendingStyledSpace release];
2436                         pendingStyledSpace = nil;
2437                     }
2438                     NSFileWrapper *fileWrapper = fileWrapperForElement(static_cast<Element *>(n));
2439                     NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWrapper:fileWrapper];
2440                     NSAttributedString *iString = [NSAttributedString attributedStringWithAttachment:attachment];
2441                     [result appendAttributedString: iString];
2442                     [attachment release];
2443                 }
2444
2445                 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()];
2446                 [result appendAttributedString: partialString];
2447                 [partialString release];
2448             }
2449         }
2450
2451         if (n == endNode)
2452             break;
2453
2454         Node *next = n->firstChild();
2455         if (!next)
2456             next = n->nextSibling();
2457
2458         while (!next && n->parentNode()) {
2459             DeprecatedString text;
2460             n = n->parentNode();
2461             if (n == endNode)
2462                 break;
2463             next = n->nextSibling();
2464
2465             if (n->hasTagName(aTag)) {
2466                 // End of a <a> element.  Create an attributed string NSLinkAttributeName attribute
2467                 // for the range of the link.  Note that we create the attributed string from the DOM, which
2468                 // will have corrected any illegally nested <a> elements.
2469                 if (linkStartNode && n == linkStartNode) {
2470                     String href = parseURL(linkStartNode->getAttribute(hrefAttr));
2471                     KURL kURL = Mac(linkStartNode->document()->frame())->completeURL(href.deprecatedString());
2472                     
2473                     NSURL *URL = kURL.getNSURL();
2474                     NSRange tempRange = { linkStartLocation, [result length]-linkStartLocation }; // workaround for 4213314
2475                     [result addAttribute:NSLinkAttributeName value:URL range:tempRange];
2476                     linkStartNode = 0;
2477                 }
2478             }
2479             else if (n->hasTagName(olTag) || n->hasTagName(ulTag)) {
2480                 if (!hasNewLine)
2481                     text += '\n';
2482                 hasNewLine = true;
2483             } else if (n->hasTagName(liTag)) {
2484                 
2485                 int i, count = listItems.count();
2486                 for (i = 0; i < count; i++){
2487                     if (listItems.at(i) == n){
2488                         listItemLocations[i].end = [result length];
2489                         break;
2490                     }
2491                 }
2492                 if (!hasNewLine)
2493                     text += '\n';
2494                 hasNewLine = true;
2495             } else if (n->hasTagName(blockquoteTag) ||
2496                        n->hasTagName(ddTag) ||
2497                        n->hasTagName(divTag) ||
2498                        n->hasTagName(dlTag) ||
2499                        n->hasTagName(dtTag) ||
2500                        n->hasTagName(hrTag) ||
2501                        n->hasTagName(listingTag) ||
2502                        n->hasTagName(preTag) ||
2503                        n->hasTagName(tdTag) ||
2504                        n->hasTagName(thTag)) {
2505                 if (!hasNewLine)
2506                     text += '\n';
2507                 hasNewLine = true;
2508             } else if (n->hasTagName(pTag) ||
2509                        n->hasTagName(trTag) ||
2510                        n->hasTagName(h1Tag) ||
2511                        n->hasTagName(h2Tag) ||
2512                        n->hasTagName(h3Tag) ||
2513                        n->hasTagName(h4Tag) ||
2514                        n->hasTagName(h5Tag) ||
2515                        n->hasTagName(h6Tag)) {
2516                 if (!hasNewLine)
2517                     text += '\n';
2518                 // An extra newline is needed at the start, not the end, of these types of tags,
2519                 // so don't add another here.
2520                 hasNewLine = true;
2521             }
2522             
2523             NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()];
2524             [result appendAttributedString:partialString];
2525             [partialString release];
2526         }
2527
2528         n = next;
2529     }
2530     
2531     [pendingStyledSpace release];
2532     
2533     // Apply paragraph styles from outside in.  This ensures that nested lists correctly
2534     // override their parent's paragraph style.
2535     {
2536         unsigned i, count = listItems.count();
2537         Element *e;
2538
2539 #ifdef POSITION_LIST
2540         Node *containingBlock;
2541         int containingBlockX, containingBlockY;
2542         
2543         // Determine the position of the outermost containing block.  All paragraph
2544         // styles and tabs should be relative to this position.  So, the horizontal position of 
2545         // each item in the list (in the resulting attributed string) will be relative to position 
2546         // of the outermost containing block.
2547         if (count > 0){
2548             containingBlock = _startNode;
2549             while (containingBlock->renderer()->isInline()){
2550                 containingBlock = containingBlock->parentNode();
2551             }
2552             containingBlock->renderer()->absolutePosition(containingBlockX, containingBlockY);
2553         }
2554 #endif
2555         
2556         for (i = 0; i < count; i++){
2557             e = listItems.at(i);
2558             info = listItemLocations[i];
2559             
2560             if (info.end < info.start)
2561                 info.end = [result length];
2562                 
2563             RenderObject *r = e->renderer();
2564             RenderStyle *style = r->style();
2565
2566             int rx;
2567             NSFont *font = style->font().getNSFont();
2568             float pointSize = [font pointSize];
2569
2570 #ifdef POSITION_LIST
2571             int ry;
2572             r->absolutePosition(rx, ry);
2573             rx -= containingBlockX;
2574             
2575             // Ensure that the text is indented at least enough to allow for the markers.
2576             rx = MAX(rx, (int)maxMarkerWidth);
2577 #else
2578             rx = (int)MAX(maxMarkerWidth, pointSize);
2579 #endif
2580
2581             // The bullet text will be right aligned at the first tab marker, followed
2582             // by a space, followed by the list item text.  The space is arbitrarily
2583             // picked as pointSize*2/3.  The space on the first line of the text item
2584             // is established by a left aligned tab, on subsequent lines it's established
2585             // by the head indent.
2586             NSMutableParagraphStyle *mps = [[NSMutableParagraphStyle alloc] init];
2587             [mps setFirstLineHeadIndent: 0];
2588             [mps setHeadIndent: rx];
2589             [mps setTabStops:[NSArray arrayWithObjects:
2590                         [[[NSTextTab alloc] initWithType:NSRightTabStopType location:rx-(pointSize*2/3)] autorelease],
2591                         [[[NSTextTab alloc] initWithType:NSLeftTabStopType location:rx] autorelease],
2592                         nil]];
2593             NSRange tempRange = { info.start, info.end-info.start }; // workaround for 4213314
2594             [result addAttribute:NSParagraphStyleAttributeName value:mps range:tempRange];
2595             [mps release];
2596         }
2597     }
2598
2599     return result;
2600
2601     END_BLOCK_OBJC_EXCEPTIONS;
2602
2603     return nil;
2604 }
2605
2606 NSImage *FrameMac::imageFromRect(NSRect rect) const
2607 {
2608     NSView *view = d->m_view->getDocumentView();
2609     if (!view)
2610         return nil;
2611     
2612     NSImage *resultImage;
2613     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2614     
2615     NSRect bounds = [view bounds];
2616     resultImage = [[[NSImage alloc] initWithSize:rect.size] autorelease];
2617
2618     if (rect.size.width != 0 && rect.size.height != 0) {
2619         [resultImage setFlipped:YES];
2620         [resultImage lockFocus];
2621
2622         CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
2623
2624         CGContextSaveGState(context);
2625         CGContextTranslateCTM(context, bounds.origin.x - rect.origin.x, bounds.origin.y - rect.origin.y);
2626         [view drawRect:rect];
2627         CGContextRestoreGState(context);
2628
2629         [resultImage unlockFocus];
2630         [resultImage setFlipped:NO];
2631     }
2632
2633     return resultImage;
2634
2635     END_BLOCK_OBJC_EXCEPTIONS;
2636     
2637     return nil;
2638 }
2639
2640 NSImage *FrameMac::selectionImage() const
2641 {
2642     d->m_drawSelectionOnly = true;  // invoke special drawing mode
2643     NSImage *result = imageFromRect(visibleSelectionRect());
2644     d->m_drawSelectionOnly = false;
2645     return result;
2646 }
2647
2648 NSImage *FrameMac::snapshotDragImage(Node *node, NSRect *imageRect, NSRect *elementRect) const
2649 {
2650     RenderObject *renderer = node->renderer();
2651     if (!renderer)
2652         return nil;
2653     
2654     renderer->updateDragState(true);    // mark dragged nodes (so they pick up the right CSS)
2655     d->m_doc->updateLayout();        // forces style recalc - needed since changing the drag state might
2656                                         // imply new styles, plus JS could have changed other things
2657     IntRect topLevelRect;
2658     NSRect paintingRect = renderer->paintingRootRect(topLevelRect);
2659
2660     d->m_elementToDraw = node;              // invoke special sub-tree drawing mode
2661     NSImage *result = imageFromRect(paintingRect);
2662     renderer->updateDragState(false);
2663     d->m_doc->updateLayout();
2664     d->m_elementToDraw = 0;
2665
2666     if (elementRect)
2667         *elementRect = topLevelRect;
2668     if (imageRect)
2669         *imageRect = paintingRect;
2670     return result;
2671 }
2672
2673 NSFont *FrameMac::fontForSelection(bool *hasMultipleFonts) const
2674 {
2675     if (hasMultipleFonts)
2676         *hasMultipleFonts = false;
2677
2678     if (!d->m_selection.isRange()) {
2679         Node *nodeToRemove;
2680         RenderStyle *style = styleForSelectionStart(nodeToRemove); // sets nodeToRemove
2681
2682         NSFont *result = 0;
2683         if (style)
2684             result = style->font().getNSFont();
2685         
2686         if (nodeToRemove) {
2687             ExceptionCode ec;
2688             nodeToRemove->remove(ec);
2689             ASSERT(ec == 0);
2690         }
2691
2692         return result;
2693     }
2694
2695     NSFont *font = nil;
2696
2697     RefPtr<Range> range = d->m_selection.toRange();
2698     Node *startNode = range->editingStartPosition().node();
2699     if (startNode != nil) {
2700         Node *pastEnd = range->pastEndNode();
2701         // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one
2702         // unreproducible case where this didn't happen, so check for nil also.
2703         for (Node *n = startNode; n && n != pastEnd; n = n->traverseNextNode()) {
2704             RenderObject *renderer = n->renderer();
2705             if (!renderer)
2706                 continue;
2707             // FIXME: Are there any node types that have renderers, but that we should be skipping?
2708             NSFont *f = renderer->style()->font().getNSFont();
2709             if (!font) {
2710                 font = f;
2711                 if (!hasMultipleFonts)
2712                     break;
2713             } else if (font != f) {
2714                 *hasMultipleFonts = true;
2715                 break;
2716             }
2717         }
2718     }
2719
2720     return font;
2721 }
2722
2723 NSDictionary *FrameMac::fontAttributesForSelectionStart() const
2724 {
2725     Node *nodeToRemove;
2726     RenderStyle *style = styleForSelectionStart(nodeToRemove);
2727     if (!style)
2728         return nil;
2729
2730     NSMutableDictionary *result = [NSMutableDictionary dictionary];
2731
2732     if (style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0)
2733         [result setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2734
2735     if (style->font().getNSFont())
2736         [result setObject:style->font().getNSFont() forKey:NSFontAttributeName];
2737
2738     if (style->color().isValid() && style->color() != Color::black)
2739         [result setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2740
2741     ShadowData *shadow = style->textShadow();
2742     if (shadow) {
2743         NSShadow *s = [[NSShadow alloc] init];
2744         [s setShadowOffset:NSMakeSize(shadow->x, shadow->y)];
2745         [s setShadowBlurRadius:shadow->blur];
2746         [s setShadowColor:nsColor(shadow->color)];
2747         [result setObject:s forKey:NSShadowAttributeName];
2748     }
2749
2750     int decoration = style->textDecorationsInEffect();
2751     if (decoration & LINE_THROUGH)
2752         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSStrikethroughStyleAttributeName];
2753
2754     int superscriptInt = 0;
2755     switch (style->verticalAlign()) {
2756         case BASELINE:
2757         case BOTTOM:
2758         case BASELINE_MIDDLE:
2759         case LENGTH:
2760         case MIDDLE:
2761         case TEXT_BOTTOM:
2762         case TEXT_TOP:
2763         case TOP:
2764             break;
2765         case SUB:
2766             superscriptInt = -1;
2767             break;
2768         case SUPER:
2769             superscriptInt = 1;
2770             break;
2771     }
2772     if (superscriptInt)
2773         [result setObject:[NSNumber numberWithInt:superscriptInt] forKey:NSSuperscriptAttributeName];
2774
2775     if (decoration & UNDERLINE)
2776         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
2777
2778     if (nodeToRemove) {
2779         ExceptionCode ec = 0;
2780         nodeToRemove->remove(ec);
2781         ASSERT(ec == 0);
2782     }
2783
2784     return result;
2785 }
2786
2787 NSWritingDirection FrameMac::baseWritingDirectionForSelectionStart() const
2788 {
2789     NSWritingDirection result = NSWritingDirectionLeftToRight;
2790
2791     Position pos = VisiblePosition(d->m_selection.start(), d->m_selection.affinity()).deepEquivalent();
2792     Node *node = pos.node();
2793     if (!node || !node->renderer() || !node->renderer()->containingBlock())
2794         return result;
2795     RenderStyle *style = node->renderer()->containingBlock()->style();
2796     if (!style)
2797         return result;
2798         
2799     switch (style->direction()) {
2800         case LTR:
2801             result = NSWritingDirectionLeftToRight;
2802             break;
2803         case RTL:
2804             result = NSWritingDirectionRightToLeft;
2805             break;
2806     }
2807
2808     return result;
2809 }
2810
2811 void FrameMac::tokenizerProcessedData()
2812 {
2813     if (d->m_doc)
2814         checkCompleted();
2815     [_bridge tokenizerProcessedData];
2816 }
2817
2818 void FrameMac::setBridge(WebCoreFrameBridge *bridge)
2819
2820     if (_bridge == bridge)
2821         return;
2822
2823     KWQRetain(bridge);
2824     KWQRelease(_bridge);
2825     _bridge = bridge;
2826 }
2827
2828 String FrameMac::overrideMediaType() const
2829 {
2830     NSString *overrideType = [_bridge overrideMediaType];
2831     if (overrideType)
2832         return overrideType;
2833     return String();
2834 }
2835
2836 void FrameMac::setDisplaysWithFocusAttributes(bool flag)
2837 {
2838     if (d->m_isFocused == flag)
2839         return;
2840     
2841     Frame::setDisplaysWithFocusAttributes(flag);
2842     Document *doc = document();
2843     // Mac Specific: Changing the tint of controls from clear to aqua/graphite and vice versa.  We
2844     // do a "fake" paint.  When the theme gets a paint call, it can then do an invalidate.
2845     if (doc && d->m_view && d->m_view->getDocumentView() && theme()->supportsControlTints() && renderer()) {
2846         doc->updateLayout(); // Ensure layout is up to date.
2847         IntRect visibleRect(enclosingIntRect(d->m_view->visibleContentRect()));
2848         GraphicsContext p;
2849         p.setUpdatingControlTints(true);
2850         paint(&p, visibleRect);
2851     }
2852 }
2853
2854 NSColor *FrameMac::bodyBackgroundColor() const
2855 {
2856     if (document() && document()->body() && document()->body()->renderer()) {
2857         Color bgColor = document()->body()->renderer()->style()->backgroundColor();
2858         if (bgColor.isValid())
2859             return nsColor(bgColor);
2860     }
2861     return nil;
2862 }
2863
2864 WebCoreKeyboardUIMode FrameMac::keyboardUIMode() const
2865 {
2866     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2867     return [_bridge keyboardUIMode];
2868     END_BLOCK_OBJC_EXCEPTIONS;
2869
2870     return WebCoreKeyboardAccessDefault;
2871 }
2872
2873 void FrameMac::didTellBridgeAboutLoad(const String& URL)
2874 {
2875     urlsBridgeKnowsAbout.add(URL.impl());
2876 }
2877
2878 bool FrameMac::haveToldBridgeAboutLoad(const String& URL)
2879 {
2880     return urlsBridgeKnowsAbout.contains(URL.impl());
2881 }
2882
2883 void FrameMac::clear()
2884 {
2885     urlsBridgeKnowsAbout.clear();
2886     setMarkedTextRange(0, nil, nil);
2887     Frame::clear();
2888 }
2889
2890 void FrameMac::print()
2891 {
2892     [Mac(this)->_bridge print];
2893 }
2894
2895 KJS::Bindings::Instance *FrameMac::getAppletInstanceForWidget(Widget *widget)
2896 {
2897     NSView *aView = widget->getView();
2898     if (!aView)
2899         return 0;
2900     jobject applet;
2901     
2902     // Get a pointer to the actual Java applet instance.
2903     if ([_bridge respondsToSelector:@selector(getAppletInView:)])
2904         applet = [_bridge getAppletInView:aView];
2905     else
2906         applet = [_bridge pollForAppletInView:aView];
2907     
2908     if (applet) {
2909         // Wrap the Java instance in a language neutral binding and hand
2910         // off ownership to the APPLET element.
2911         KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2912         KJS::Bindings::Instance *instance = KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::JavaLanguage, applet, executionContext);        
2913         return instance;
2914     }
2915     
2916     return 0;
2917 }
2918
2919 static KJS::Bindings::Instance *getInstanceForView(NSView *aView)
2920 {
2921     if ([aView respondsToSelector:@selector(objectForWebScript)]){
2922         id object = [aView objectForWebScript];
2923         if (object) {
2924             KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2925             return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::ObjectiveCLanguage, object, executionContext);
2926         }
2927     }
2928     else if ([aView respondsToSelector:@selector(pluginScriptableObject)]){
2929         void *object = [aView pluginScriptableObject];
2930         if (object) {
2931             KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2932             return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::CLanguage, object, executionContext);
2933         }
2934     }
2935     return 0;
2936 }
2937
2938 KJS::Bindings::Instance *FrameMac::getEmbedInstanceForWidget(Widget *widget)
2939 {
2940     return getInstanceForView(widget->getView());
2941 }
2942
2943 KJS::Bindings::Instance *FrameMac::getObjectInstanceForWidget(Widget *widget)
2944 {
2945     return getInstanceForView(widget->getView());
2946 }
2947
2948 void FrameMac::addPluginRootObject(KJS::Bindings::RootObject *root)
2949 {
2950     m_rootObjects.append(root);
2951 }
2952
2953 void FrameMac::cleanupPluginRootObjects()
2954 {
2955     JSLock lock;
2956
2957     unsigned count = m_rootObjects.size();
2958     for (unsigned i = 0; i < count; i++)
2959         m_rootObjects[i]->removeAllNativeReferences();
2960     m_rootObjects.clear();
2961 }
2962
2963 void FrameMac::registerCommandForUndoOrRedo(const EditCommandPtr &cmd, bool isRedo)
2964 {
2965     ASSERT(cmd.get());
2966     KWQEditCommand *kwq = [KWQEditCommand commandWithEditCommand:cmd.get()];
2967     NSUndoManager *undoManager = [_bridge undoManager];
2968     [undoManager registerUndoWithTarget:_bridge selector:(isRedo ? @selector(redoEditing:) : @selector(undoEditing:)) object:kwq];
2969     NSString *actionName = [_bridge nameForUndoAction:static_cast<WebUndoAction>(cmd.editingAction())];
2970     if (actionName)
2971         [undoManager setActionName:actionName];
2972     _haveUndoRedoOperations = YES;
2973 }
2974
2975 void FrameMac::registerCommandForUndo(const EditCommandPtr &cmd)
2976 {
2977     registerCommandForUndoOrRedo(cmd, NO);
2978 }
2979
2980 void FrameMac::registerCommandForRedo(const EditCommandPtr &cmd)
2981 {
2982     registerCommandForUndoOrRedo(cmd, YES);
2983 }
2984
2985 void FrameMac::clearUndoRedoOperations()
2986 {
2987     if (_haveUndoRedoOperations) {
2988         [[_bridge undoManager] removeAllActionsWithTarget:_bridge];
2989         _haveUndoRedoOperations = NO;
2990     }
2991 }
2992
2993 void FrameMac::issueUndoCommand()
2994 {
2995     if (canUndo())
2996         [[_bridge undoManager] undo];
2997 }
2998
2999 void FrameMac::issueRedoCommand()
3000 {
3001     if (canRedo())
3002         [[_bridge undoManager] redo];
3003 }
3004
3005 void FrameMac::issueCutCommand()
3006 {
3007     [_bridge issueCutCommand];
3008 }
3009
3010 void FrameMac::issueCopyCommand()
3011 {
3012     [_bridge issueCopyCommand];
3013 }
3014
3015 void FrameMac::issuePasteCommand()
3016 {
3017     [_bridge issuePasteCommand];
3018 }
3019
3020 void FrameMac::issuePasteAndMatchStyleCommand()
3021 {
3022     [_bridge issuePasteAndMatchStyleCommand];
3023 }
3024
3025 void FrameMac::issueTransposeCommand()
3026 {
3027     [_bridge issueTransposeCommand];
3028 }
3029
3030 bool FrameMac::canUndo() const
3031 {
3032     return [[Mac(this)->_bridge undoManager] canUndo];
3033 }
3034
3035 bool FrameMac::canRedo() const
3036 {
3037     return [[Mac(this)->_bridge undoManager] canRedo];
3038 }
3039
3040 bool FrameMac::canPaste() const
3041 {
3042     return [Mac(this)->_bridge canPaste];
3043 }
3044
3045 void FrameMac::markMisspellingsInAdjacentWords(const VisiblePosition &p)
3046 {
3047     if (![_bridge isContinuousSpellCheckingEnabled])
3048         return;
3049     markMisspellings(SelectionController(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)));
3050 }
3051
3052 void FrameMac::markMisspellings(const SelectionController &selection)
3053 {
3054     // This function is called with a selection already expanded to word boundaries.
3055     // Might be nice to assert that here.
3056
3057     if (![_bridge isContinuousSpellCheckingEnabled])
3058         return;
3059
3060     RefPtr<Range> searchRange(selection.toRange());
3061     if (!searchRange || searchRange->isDetached())
3062         return;
3063     
3064     // If we're not in an editable node, bail.
3065     int exception = 0;
3066     Node *editableNode = searchRange->startContainer(exception);
3067     if (!editableNode->isContentEditable())
3068         return;
3069     
3070     // Get the spell checker if it is available
3071     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
3072     if (checker == nil)
3073         return;
3074     
3075     WordAwareIterator it(searchRange.get());
3076     
3077     while (!it.atEnd()) {      // we may be starting at the end of the doc, and already by atEnd
3078         const QChar *chars = it.characters();
3079         int len = it.length();
3080         if (len > 1 || !chars[0].isSpace()) {
3081             NSString *chunk = [[NSString alloc] initWithCharactersNoCopy:(unichar *)chars length:len freeWhenDone:NO];
3082             int startIndex = 0;
3083             // Loop over the chunk to find each misspelling in it.
3084             while (startIndex < len) {
3085                 NSRange misspelling = [checker checkSpellingOfString:chunk startingAt:startIndex language:nil wrap:NO inSpellDocumentWithTag:[_bridge spellCheckerDocumentTag] wordCount:NULL];
3086                 if (misspelling.length == 0)
3087                     break;
3088                 else {
3089                     // Build up result range and string.  Note the misspelling may span many text nodes,
3090                     // but the CharIterator insulates us from this complexity
3091                     RefPtr<Range> misspellingRange(rangeOfContents(document()));
3092                     CharacterIterator chars(it.range().get());
3093                     chars.advance(misspelling.location);
3094                     misspellingRange->setStart(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
3095                     chars.advance(misspelling.length);
3096                     misspellingRange->setEnd(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
3097                     // Mark misspelling in document.
3098                     document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
3099                     startIndex = misspelling.location + misspelling.length;
3100                 }
3101             }
3102             [chunk release];
3103         }
3104     
3105         it.advance();
3106     }
3107 }
3108
3109 void FrameMac::respondToChangedSelection(const SelectionController &oldSelection, bool closeTyping)
3110 {
3111     if (document()) {
3112         if ([_bridge isContinuousSpellCheckingEnabled]) {
3113             SelectionController oldAdjacentWords = SelectionController();
3114             
3115             // If this is a change in selection resulting from a delete operation, oldSelection may no longer
3116             // be in the document.
3117             if (oldSelection.start().node() && oldSelection.start().node()->inDocument()) {
3118                 VisiblePosition oldStart(oldSelection.start(), oldSelection.affinity());
3119                 oldAdjacentWords = SelectionController(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));   
3120             }
3121
3122             VisiblePosition newStart(selection().start(), selection().affinity());
3123             SelectionController newAdjacentWords(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
3124
3125             if (oldAdjacentWords != newAdjacentWords) {
3126                 // Mark misspellings in the portion that was previously unmarked because of
3127                 // the proximity of the start of the selection. We only spell check words in
3128                 // the vicinity of the start of the old selection because the spelling checker
3129                 // is not fast enough to do a lot of spelling checking implicitly. This matches
3130                 // AppKit. This function is really the only code that knows that rule. The
3131                 // markMisspellings function is prepared to handler larger ranges.
3132
3133                 // When typing we check spelling elsewhere, so don't redo it here.
3134                 if (closeTyping)
3135                     markMisspellings(oldAdjacentWords);
3136
3137                 // This only erases a marker in the first word of the selection.
3138                 // Perhaps peculiar, but it matches AppKit.
3139                 document()->removeMarkers(newAdjacentWords.toRange().get(), DocumentMarker::Spelling);
3140             }
3141         } else
3142             // When continuous spell checking is off, no markers appear after the selection changes.
3143             document()->removeMarkers(DocumentMarker::Spelling);
3144     }
3145
3146     [_bridge respondToChangedSelection];
3147 }
3148
3149 bool FrameMac::shouldChangeSelection(const SelectionController &oldSelection, const SelectionController &newSelection, EAffinity affinity, bool stillSelecting) const
3150 {
3151     return [_bridge shouldChangeSelectedDOMRange:[DOMRange _rangeWith:oldSelection.toRange().get()]
3152                                       toDOMRange:[DOMRange _rangeWith:newSelection.toRange().get()]
3153                                         affinity:static_cast<NSSelectionAffinity>(affinity)
3154                                   stillSelecting:stillSelecting];
3155 }
3156
3157 void FrameMac::respondToChangedContents()
3158 {
3159     if (AccessibilityObjectCache::accessibilityEnabled())
3160         renderer()->document()->getAccObjectCache()->postNotificationToTopWebArea(renderer(), "AXValueChanged");
3161     [_bridge respondToChangedContents];
3162 }
3163
3164 bool FrameMac::isContentEditable() const
3165 {
3166     return Frame::isContentEditable() || [_bridge isEditable];
3167 }
3168
3169 bool FrameMac::shouldBeginEditing(const Range *range) const
3170 {
3171     ASSERT(range);
3172     return [_bridge shouldBeginEditing:[DOMRange _rangeWith:const_cast<Range *>(range)]];
3173 }
3174
3175 bool FrameMac::shouldEndEditing(const Range *range) const
3176 {
3177     ASSERT(range);
3178     return [_bridge shouldEndEditing:[DOMRange _rangeWith:const_cast<Range *>(range)]];
3179 }
3180
3181 void FrameMac::didBeginEditing() const
3182 {
3183     [_bridge didBeginEditing];
3184 }
3185
3186 void FrameMac::didEndEditing() const
3187 {
3188     [_bridge didEndEditing];
3189 }
3190
3191 static DeprecatedValueList<MarkedTextUnderline> convertAttributesToUnderlines(const Range *markedTextRange, NSArray *attributes, NSArray *ranges)
3192 {
3193     DeprecatedValueList<MarkedTextUnderline> result;
3194
3195     int exception = 0;
3196     int baseOffset = markedTextRange->startOffset(exception);
3197
3198     unsigned length = [attributes count];
3199     ASSERT([ranges count] == length);
3200
3201     for (unsigned i = 0; i < length; i++) {
3202         NSNumber *style = [[attributes objectAtIndex:i] objectForKey:NSUnderlineStyleAttributeName];
3203         if (!style)
3204             continue;
3205         NSRange range = [[ranges objectAtIndex:i] rangeValue];
3206         NSColor *color = [[attributes objectAtIndex:i] objectForKey:NSUnderlineColorAttributeName];
3207         Color qColor = Color::black;
3208         if (color) {
3209             NSColor* deviceColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
3210             qColor = Color(makeRGBA((int)(255 * [deviceColor redComponent]),
3211                                     (int)(255 * [deviceColor blueComponent]),
3212                                     (int)(255 * [deviceColor greenComponent]),
3213                                     (int)(255 * [deviceColor alphaComponent])));
3214         }
3215
3216         result.append(MarkedTextUnderline(range.location + baseOffset, 
3217                                           range.location + baseOffset + range.length, 
3218                                           qColor,
3219                                           [style intValue] > 1));
3220     }
3221
3222     return result;
3223 }
3224
3225 void FrameMac::setMarkedTextRange(const Range *range, NSArray *attributes, NSArray *ranges)
3226 {
3227     int exception = 0;
3228
3229     ASSERT(!range || range->startContainer(exception) == range->endContainer(exception));
3230     ASSERT(!range || range->collapsed(exception) || range->startContainer(exception)->isTextNode());
3231
3232     if (attributes == nil) {
3233         d->m_markedTextUsesUnderlines = false;
3234         d->m_markedTextUnderlines.clear();
3235     } else {
3236         d->m_markedTextUsesUnderlines = true;
3237         d->m_markedTextUnderlines = convertAttributesToUnderlines(range, attributes, ranges);
3238     }
3239
3240     if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
3241         m_markedTextRange->startContainer(exception)->renderer()->repaint();
3242
3243     if (range && range->collapsed(exception))
3244         m_markedTextRange = 0;
3245     else
3246         m_markedTextRange = const_cast<Range *>(range);
3247
3248     if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
3249         m_markedTextRange->startContainer(exception)->renderer()->repaint();
3250 }
3251
3252 bool FrameMac::canGoBackOrForward(int distance) const
3253 {
3254     return [_bridge canGoBackOrForward:distance];
3255 }
3256
3257 void FrameMac::didFirstLayout()
3258 {
3259     [_bridge didFirstLayout];
3260 }
3261
3262 NSMutableDictionary *FrameMac::dashboardRegionsDictionary()
3263 {
3264     Document *doc = document();
3265     if (!doc)
3266         return nil;
3267
3268     const DeprecatedValueList<DashboardRegionValue> regions = doc->dashboardRegions();
3269     unsigned i, count = regions.count();
3270
3271     // Convert the DeprecatedValueList<DashboardRegionValue> into a NSDictionary of WebDashboardRegions
3272     NSMutableDictionary *webRegions = [[[NSMutableDictionary alloc] initWithCapacity:count] autorelease];
3273     for (i = 0; i < count; i++) {
3274         DashboardRegionValue region = regions[i];
3275
3276         if (region.type == StyleDashboardRegion::None)
3277             continue;
3278         
3279         NSString *label = region.label;
3280         WebDashboardRegionType type = WebDashboardRegionTypeNone;
3281         if (region.type == StyleDashboardRegion::Circle)
3282             type = WebDashboardRegionTypeCircle;
3283         else if (region.type == StyleDashboardRegion::Rectangle)
3284             type = WebDashboardRegionTypeRectangle;
3285         NSMutableArray *regionValues = [webRegions objectForKey:label];
3286         if (!regionValues) {
3287             regionValues = [NSMutableArray array];
3288             [webRegions setObject:regionValues forKey:label];
3289         }
3290         
3291         WebDashboardRegion *webRegion = [[[WebDashboardRegion alloc] initWithRect:region.bounds clip:region.clip type:type] autorelease];
3292         [regionValues addObject:webRegion];
3293     }
3294     
3295     return webRegions;
3296 }
3297
3298 void FrameMac::dashboardRegionsChanged()
3299 {
3300     NSMutableDictionary *webRegions = dashboardRegionsDictionary();
3301     [_bridge dashboardRegionsChanged:webRegions];
3302 }
3303
3304 bool FrameMac::isCharacterSmartReplaceExempt(const QChar &c, bool isPreviousChar)
3305 {
3306     return [_bridge isCharacterSmartReplaceExempt:c.unicode() isPreviousCharacter:isPreviousChar];
3307 }
3308
3309 void FrameMac::handledOnloadEvents()
3310 {
3311     [_bridge handledOnloadEvents];
3312 }
3313
3314 bool FrameMac::shouldClose()
3315 {
3316     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3317
3318     if (![_bridge canRunBeforeUnloadConfirmPanel])
3319         return true;
3320
3321     RefPtr<Document> doc = document();
3322     if (!doc)
3323         return true;
3324     HTMLElement* body = doc->body();
3325     if (!body)
3326         return true;
3327
3328     RefPtr<BeforeUnloadEvent> event = new BeforeUnloadEvent;
3329     event->setTarget(doc.get());
3330     doc->handleWindowEvent(event.get(), false);
3331
3332     if (!event->defaultPrevented() && doc)
3333         doc->defaultEventHandler(event.get());
3334     if (event->result().isNull())
3335         return true;
3336
3337     String text = event->result();
3338     text.replace(QChar('\\'), backslashAsCurrencySymbol());
3339
3340     return [_bridge runBeforeUnloadConfirmPanelWithMessage:text];
3341
3342     END_BLOCK_OBJC_EXCEPTIONS;
3343
3344     return true;
3345 }
3346
3347 void FrameMac::dragSourceMovedTo(const PlatformMouseEvent& event)
3348 {
3349     if (_dragSrc && _dragSrcMayBeDHTML)
3350         // for now we don't care if event handler cancels default behavior, since there is none
3351         dispatchDragSrcEvent(dragEvent, event);
3352 }
3353
3354 void FrameMac::dragSourceEndedAt(const PlatformMouseEvent& event, NSDragOperation operation)
3355 {
3356     if (_dragSrc && _dragSrcMayBeDHTML) {
3357         _dragClipboard->setDestinationOperation(operation);
3358         // for now we don't care if event handler cancels default behavior, since there is none
3359         dispatchDragSrcEvent(dragendEvent, event);
3360     }
3361     freeClipboard();
3362     _dragSrc = 0;
3363 }
3364
3365 // returns if we should continue "default processing", i.e., whether eventhandler canceled
3366 bool FrameMac::dispatchDragSrcEvent(const AtomicString &eventType, const PlatformMouseEvent& event) const
3367 {
3368     bool noDefaultProc = d->m_view->dispatchDragEvent(eventType, _dragSrc.get(), event, _dragClipboard.get());
3369     return !noDefaultProc;
3370 }
3371
3372 void Frame::setNeedsReapplyStyles()
3373 {
3374     [Mac(this)->bridge() setNeedsReapplyStyles];
3375 }
3376
3377 }