WebCore:
[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 "AXObjectCache.h"
30 #import "BeforeUnloadEvent.h"
31 #import "BlockExceptions.h"
32 #import "BrowserExtensionMac.h"
33 #import "CSSComputedStyleDeclaration.h"
34 #import "Cache.h"
35 #import "ClipboardEvent.h"
36 #import "ClipboardMac.h"
37 #import "Cursor.h"
38 #import "DOMInternal.h"
39 #import "DOMWindow.h"
40 #import "DocumentLoader.h"
41 #import "Event.h"
42 #import "EventNames.h"
43 #import "FloatRect.h"
44 #import "FontData.h"
45 #import "FoundationExtras.h"
46 #import "FrameLoadRequest.h"
47 #import "FrameLoader.h"
48 #import "FrameLoaderClient.h"
49 #import "FrameLoaderTypes.h"
50 #import "FramePrivate.h"
51 #import "GraphicsContext.h"
52 #import "HTMLDocument.h"
53 #import "HTMLFormElement.h"
54 #import "HTMLFrameElement.h"
55 #import "HTMLGenericFormElement.h"
56 #import "HTMLInputElement.h"
57 #import "HTMLNames.h"
58 #import "HTMLTableCellElement.h"
59 #import "HitTestResult.h"
60 #import "Logging.h"
61 #import "MouseEventWithHitTestResults.h"
62 #import "Page.h"
63 #import "PlatformKeyboardEvent.h"
64 #import "PlatformScrollBar.h"
65 #import "PlatformWheelEvent.h"
66 #import "Plugin.h"
67 #import "RegularExpression.h"
68 #import "RenderImage.h"
69 #import "RenderListItem.h"
70 #import "RenderPart.h"
71 #import "RenderTableCell.h"
72 #import "RenderTheme.h"
73 #import "RenderView.h"
74 #import "ResourceHandle.h"
75 #import "SystemTime.h"
76 #import "TextIterator.h"
77 #import "TextResourceDecoder.h"
78 #import "WebCoreEditCommand.h"
79 #import "WebCoreFrameBridge.h"
80 #import "WebCoreSystemInterface.h"
81 #import "WebCoreViewFactory.h"
82 #import "WebDashboardRegion.h"
83 #import "WebScriptObjectPrivate.h"
84 #import "csshelper.h"
85 #import "htmlediting.h"
86 #import "kjs_window.h"
87 #import "visible_units.h"
88 #import <Carbon/Carbon.h>
89 #import <JavaScriptCore/NP_jsobject.h>
90 #import <JavaScriptCore/npruntime_impl.h>
91
92 #undef _webcore_TIMING
93
94 @interface NSObject (WebPlugIn)
95 - (id)objectForWebScript;
96 - (NPObject *)createPluginScriptableObject;
97 @end
98
99 using namespace std;
100 using namespace KJS::Bindings;
101
102 using KJS::JSLock;
103
104 namespace WebCore {
105
106 using namespace EventNames;
107 using namespace HTMLNames;
108
109 NSEvent* FrameMac::_currentEvent = nil;
110
111 static const unsigned int escChar = 27;
112 static SEL selectorForKeyEvent(const PlatformKeyboardEvent* event)
113 {
114     // FIXME: This helper function is for the autofill code so the bridge can pass a selector to the form delegate.  
115     // Eventually, we should move all of the autofill code down to WebKit and remove the need for this function by
116     // not relying on the selector in the new implementation.
117     String key = event->unmodifiedText();
118     if (key.length() != 1)
119         return 0;
120
121     SEL selector = NULL;
122     switch (key[0U]) {
123     case NSUpArrowFunctionKey:
124         selector = @selector(moveUp:); break;
125     case NSDownArrowFunctionKey:
126         selector = @selector(moveDown:); break;
127     case escChar:
128         selector = @selector(cancel:); break;
129     case NSTabCharacter:
130         selector = @selector(insertTab:); break;
131     case NSBackTabCharacter:
132         selector = @selector(insertBacktab:); break;
133     case NSNewlineCharacter:
134     case NSCarriageReturnCharacter:
135     case NSEnterCharacter:
136         selector = @selector(insertNewline:); break;
137         break;
138     }
139     return selector;
140 }
141
142 FrameMac::FrameMac(Page* page, Element* ownerElement, PassRefPtr<EditorClient> client)
143     : Frame(page, ownerElement, client)
144     , _bridge(nil)
145     , _mouseDownView(nil)
146     , _sendingEventToSubview(false)
147     , _mouseDownMayStartSelect(false)
148     , _activationEventNumber(0)
149     , _bindingRoot(0)
150     , _windowScriptObject(0)
151     , _windowScriptNPObject(0)
152 {
153     d->m_extension = new BrowserExtensionMac(this);
154 }
155
156 FrameMac::~FrameMac()
157 {
158     setView(0);
159     freeClipboard();
160     clearRecordedFormValues();    
161     
162     [_bridge clearFrame];
163     HardRelease(_bridge);
164     _bridge = nil;
165
166     cancelAndClear();
167 }
168
169 #pragma mark BEGIN LOADING FUNCTIONS
170
171 void FrameMac::submitForm(const FrameLoadRequest& request)
172 {
173     BEGIN_BLOCK_OBJC_EXCEPTIONS;
174
175     // FIXME: We'd like to remove this altogether and fix the multiple form submission issue another way.
176     // We do not want to submit more than one form from the same page,
177     // nor do we want to submit a single form more than once.
178     // This flag prevents these from happening; not sure how other browsers prevent this.
179     // The flag is reset in each time we start handle a new mouse or key down event, and
180     // also in setView since this part may get reused for a page from the back/forward cache.
181     // The form multi-submit logic here is only needed when we are submitting a form that affects this frame.
182     // FIXME: Frame targeting is only one of the ways the submission could end up doing something other
183     // than replacing this frame's content, so this check is flawed. On the other hand, the check is hardly
184     // needed any more now that we reset d->m_submittedFormURL on each mouse or key down event.
185     Frame* target = request.m_frameName.isEmpty() ? this : tree()->find(request.m_frameName);
186     bool willReplaceThisFrame = false;
187     for (Frame* p = this; p; p = p->tree()->parent()) {
188         if (p == target) {
189             willReplaceThisFrame = true;
190             break;
191         }
192     }
193     if (willReplaceThisFrame) {
194         if (d->m_submittedFormURL == request.m_request.url())
195             return;
196         d->m_submittedFormURL = request.m_request.url();
197     }
198
199     loader()->load(request, true, _currentEvent,
200         d->m_formAboutToBeSubmitted.get(), d->m_formValuesAboutToBeSubmitted);
201
202     clearRecordedFormValues();
203
204     END_BLOCK_OBJC_EXCEPTIONS;
205 }
206
207 void FrameMac::urlSelected(const FrameLoadRequest& request, const Event* /*triggeringEvent*/)
208 {
209     FrameLoadRequest copy = request;
210     if (copy.m_request.httpReferrer().isEmpty())
211         copy.m_request.setHTTPReferrer(referrer());
212
213     // FIXME: How do we know that userGesture is always true?
214     loader()->load(copy, true, _currentEvent, 0, HashMap<String, String>());
215 }
216
217 Frame* FrameMac::createFrame(const KURL& url, const String& name, Element* ownerElement, const String& referrer)
218 {
219     BEGIN_BLOCK_OBJC_EXCEPTIONS;
220     
221     BOOL allowsScrolling = YES;
222     int marginWidth = -1;
223     int marginHeight = -1;
224     if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) {
225         HTMLFrameElement* o = static_cast<HTMLFrameElement*>(ownerElement);
226         allowsScrolling = o->scrollingMode() != ScrollbarAlwaysOff;
227         marginWidth = o->getMarginWidth();
228         marginHeight = o->getMarginHeight();
229     }
230
231     WebCoreFrameBridge *childBridge = [_bridge createChildFrameNamed:name
232                                                              withURL:url.getNSURL()
233                                                             referrer:referrer 
234                                                           ownerElement:ownerElement
235                                                      allowsScrolling:allowsScrolling
236                                                          marginWidth:marginWidth
237                                                         marginHeight:marginHeight];
238     return [childBridge _frame];
239
240     END_BLOCK_OBJC_EXCEPTIONS;
241     return 0;
242 }
243
244 #pragma mark END LOADING FUNCTIONS
245
246 void FrameMac::freeClipboard()
247 {
248     if (_dragClipboard)
249         _dragClipboard->setAccessPolicy(ClipboardNumb);
250 }
251
252 // Either get cached regexp or build one that matches any of the labels.
253 // The regexp we build is of the form:  (STR1|STR2|STRN)
254 RegularExpression *regExpForLabels(NSArray *labels)
255 {
256     // All the ObjC calls in this method are simple array and string
257     // calls which we can assume do not raise exceptions
258
259
260     // Parallel arrays that we use to cache regExps.  In practice the number of expressions
261     // that the app will use is equal to the number of locales is used in searching.
262     static const unsigned int regExpCacheSize = 4;
263     static NSMutableArray *regExpLabels = nil;
264     static Vector<RegularExpression*> regExps;
265     static RegularExpression wordRegExp = RegularExpression("\\w");
266
267     RegularExpression *result;
268     if (!regExpLabels)
269         regExpLabels = [[NSMutableArray alloc] initWithCapacity:regExpCacheSize];
270     CFIndex cacheHit = [regExpLabels indexOfObject:labels];
271     if (cacheHit != NSNotFound)
272         result = regExps.at(cacheHit);
273     else {
274         DeprecatedString pattern("(");
275         unsigned int numLabels = [labels count];
276         unsigned int i;
277         for (i = 0; i < numLabels; i++) {
278             DeprecatedString label = DeprecatedString::fromNSString((NSString *)[labels objectAtIndex:i]);
279
280             bool startsWithWordChar = false;
281             bool endsWithWordChar = false;
282             if (label.length() != 0) {
283                 startsWithWordChar = wordRegExp.search(label.at(0)) >= 0;
284                 endsWithWordChar = wordRegExp.search(label.at(label.length() - 1)) >= 0;
285             }
286             
287             if (i != 0)
288                 pattern.append("|");
289             // Search for word boundaries only if label starts/ends with "word characters".
290             // If we always searched for word boundaries, this wouldn't work for languages
291             // such as Japanese.
292             if (startsWithWordChar) {
293                 pattern.append("\\b");
294             }
295             pattern.append(label);
296             if (endsWithWordChar) {
297                 pattern.append("\\b");
298             }
299         }
300         pattern.append(")");
301         result = new RegularExpression(pattern, false);
302     }
303
304     // add regexp to the cache, making sure it is at the front for LRU ordering
305     if (cacheHit != 0) {
306         if (cacheHit != NSNotFound) {
307             // remove from old spot
308             [regExpLabels removeObjectAtIndex:cacheHit];
309             regExps.remove(cacheHit);
310         }
311         // add to start
312         [regExpLabels insertObject:labels atIndex:0];
313         regExps.insert(0, result);
314         // trim if too big
315         if ([regExpLabels count] > regExpCacheSize) {
316             [regExpLabels removeObjectAtIndex:regExpCacheSize];
317             RegularExpression *last = regExps.last();
318             regExps.removeLast();
319             delete last;
320         }
321     }
322     return result;
323 }
324
325 NSString* FrameMac::searchForLabelsAboveCell(RegularExpression* regExp, HTMLTableCellElement* cell)
326 {
327     RenderTableCell* cellRenderer = static_cast<RenderTableCell*>(cell->renderer());
328
329     if (cellRenderer && cellRenderer->isTableCell()) {
330         RenderTableCell* cellAboveRenderer = cellRenderer->table()->cellAbove(cellRenderer);
331
332         if (cellAboveRenderer) {
333             HTMLTableCellElement *aboveCell =
334                 static_cast<HTMLTableCellElement*>(cellAboveRenderer->element());
335
336             if (aboveCell) {
337                 // search within the above cell we found for a match
338                 for (Node *n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) {
339                     if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
340                         // For each text chunk, run the regexp
341                         DeprecatedString nodeString = n->nodeValue().deprecatedString();
342                         int pos = regExp->searchRev(nodeString);
343                         if (pos >= 0)
344                             return nodeString.mid(pos, regExp->matchedLength()).getNSString();
345                     }
346                 }
347             }
348         }
349     }
350     // Any reason in practice to search all cells in that are above cell?
351     return nil;
352 }
353
354 NSString *FrameMac::searchForLabelsBeforeElement(NSArray *labels, Element *element)
355 {
356     RegularExpression *regExp = regExpForLabels(labels);
357     // We stop searching after we've seen this many chars
358     const unsigned int charsSearchedThreshold = 500;
359     // This is the absolute max we search.  We allow a little more slop than
360     // charsSearchedThreshold, to make it more likely that we'll search whole nodes.
361     const unsigned int maxCharsSearched = 600;
362     // If the starting element is within a table, the cell that contains it
363     HTMLTableCellElement *startingTableCell = 0;
364     bool searchedCellAbove = false;
365
366     // walk backwards in the node tree, until another element, or form, or end of tree
367     int unsigned lengthSearched = 0;
368     Node *n;
369     for (n = element->traversePreviousNode();
370          n && lengthSearched < charsSearchedThreshold;
371          n = n->traversePreviousNode())
372     {
373         if (n->hasTagName(formTag)
374             || (n->isHTMLElement()
375                 && static_cast<HTMLElement*>(n)->isGenericFormElement()))
376         {
377             // We hit another form element or the start of the form - bail out
378             break;
379         } else if (n->hasTagName(tdTag) && !startingTableCell) {
380             startingTableCell = static_cast<HTMLTableCellElement*>(n);
381         } else if (n->hasTagName(trTag) && startingTableCell) {
382             NSString *result = searchForLabelsAboveCell(regExp, startingTableCell);
383             if (result) {
384                 return result;
385             }
386             searchedCellAbove = true;
387         } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
388             // For each text chunk, run the regexp
389             DeprecatedString nodeString = n->nodeValue().deprecatedString();
390             // add 100 for slop, to make it more likely that we'll search whole nodes
391             if (lengthSearched + nodeString.length() > maxCharsSearched)
392                 nodeString = nodeString.right(charsSearchedThreshold - lengthSearched);
393             int pos = regExp->searchRev(nodeString);
394             if (pos >= 0)
395                 return nodeString.mid(pos, regExp->matchedLength()).getNSString();
396             else
397                 lengthSearched += nodeString.length();
398         }
399     }
400
401     // If we started in a cell, but bailed because we found the start of the form or the
402     // previous element, we still might need to search the row above us for a label.
403     if (startingTableCell && !searchedCellAbove) {
404          return searchForLabelsAboveCell(regExp, startingTableCell);
405     } else {
406         return nil;
407     }
408 }
409
410 NSString *FrameMac::matchLabelsAgainstElement(NSArray *labels, Element *element)
411 {
412     DeprecatedString name = element->getAttribute(nameAttr).deprecatedString();
413     // Make numbers and _'s in field names behave like word boundaries, e.g., "address2"
414     name.replace(RegularExpression("[[:digit:]]"), " ");
415     name.replace('_', ' ');
416     
417     RegularExpression *regExp = regExpForLabels(labels);
418     // Use the largest match we can find in the whole name string
419     int pos;
420     int length;
421     int bestPos = -1;
422     int bestLength = -1;
423     int start = 0;
424     do {
425         pos = regExp->search(name, start);
426         if (pos != -1) {
427             length = regExp->matchedLength();
428             if (length >= bestLength) {
429                 bestPos = pos;
430                 bestLength = length;
431             }
432             start = pos+1;
433         }
434     } while (pos != -1);
435
436     if (bestPos != -1)
437         return name.mid(bestPos, bestLength).getNSString();
438     return nil;
439 }
440
441 void FrameMac::frameDetached()
442 {
443     Frame::frameDetached();
444
445     BEGIN_BLOCK_OBJC_EXCEPTIONS;
446     [_bridge frameDetached];
447     END_BLOCK_OBJC_EXCEPTIONS;
448 }
449
450 ObjectContentType FrameMac::objectContentType(const KURL& url, const String& mimeType)
451 {
452     return (ObjectContentType)[_bridge determineObjectFromMIMEType:mimeType URL:url.getNSURL()];
453 }
454
455 static NSArray* nsArray(const Vector<String>& vector)
456 {
457     unsigned len = vector.size();
458     NSMutableArray* array = [NSMutableArray arrayWithCapacity:len];
459     for (unsigned x = 0; x < len; x++)
460         [array addObject:vector[x]];
461     return array;
462 }
463
464 Plugin* FrameMac::createPlugin(Element* element, const KURL& url, const Vector<String>& paramNames, const Vector<String>& paramValues, const String& mimeType)
465 {
466     BEGIN_BLOCK_OBJC_EXCEPTIONS;
467
468     return new Plugin(new Widget([_bridge viewForPluginWithURL:url.getNSURL()
469                                   attributeNames:nsArray(paramNames)
470                                   attributeValues:nsArray(paramValues)
471                                   MIMEType:mimeType
472                                   DOMElement:(element ? [DOMElement _elementWith:element] : nil)
473                                 loadManually:d->m_doc->isPluginDocument()]));
474
475     END_BLOCK_OBJC_EXCEPTIONS;
476     return 0;
477 }
478
479 void FrameMac::redirectDataToPlugin(Widget* pluginWidget)
480 {
481     [_bridge redirectDataToPlugin:pluginWidget->getView()];
482 }
483
484
485 void FrameMac::setView(FrameView *view)
486 {
487     Frame::setView(view);
488     
489     // Only one form submission is allowed per view of a part.
490     // Since this part may be getting reused as a result of being
491     // pulled from the back/forward cache, reset this flag.
492     d->m_submittedFormURL = KURL();
493 }
494
495 // FIXME: Remove this method; it's superfluous.
496 void FrameMac::setTitle(const String &title)
497 {
498     loader()->documentLoader()->setTitle(title);
499 }
500
501 void FrameMac::setStatusBarText(const String& status)
502 {
503     String text = status;
504     text.replace('\\', backslashAsCurrencySymbol());
505     
506     // We want the temporaries allocated here to be released even before returning to the 
507     // event loop; see <http://bugzilla.opendarwin.org/show_bug.cgi?id=9880>.
508     NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init];
509
510     BEGIN_BLOCK_OBJC_EXCEPTIONS;
511     [_bridge setStatusText:text];
512     END_BLOCK_OBJC_EXCEPTIONS;
513
514     [localPool release];
515 }
516
517 void FrameMac::scheduleClose()
518 {
519     if (!shouldClose())
520         return;
521     BEGIN_BLOCK_OBJC_EXCEPTIONS;
522     [_bridge closeWindowSoon];
523     END_BLOCK_OBJC_EXCEPTIONS;
524 }
525
526 void FrameMac::focusWindow()
527 {
528     BEGIN_BLOCK_OBJC_EXCEPTIONS;
529
530     // If we're a top level window, bring the window to the front.
531     if (!tree()->parent())
532         [_bridge activateWindow];
533
534     // Might not have a view yet: this could be a child frame that has not yet received its first byte of data.
535     // FIXME: Should remember that the frame needs focus.  See <rdar://problem/4645685>.
536     if (d->m_view) {
537         NSView *view = d->m_view->getDocumentView();
538         if ([_bridge firstResponder] != view)
539             [_bridge makeFirstResponder:view];
540     }
541
542     END_BLOCK_OBJC_EXCEPTIONS;
543 }
544
545 void FrameMac::unfocusWindow()
546 {
547     // Might not have a view yet: this could be a child frame that has not yet received its first byte of data.
548     // FIXME: Should remember that the frame needs to unfocus.  See <rdar://problem/4645685>.
549     if (!d->m_view)
550         return;
551
552     BEGIN_BLOCK_OBJC_EXCEPTIONS;
553     NSView *view = d->m_view->getDocumentView();
554     if ([_bridge firstResponder] == view) {
555         // If we're a top level window, deactivate the window.
556         if (!tree()->parent())
557             [_bridge deactivateWindow];
558         else {
559             // We want to shift focus to our parent.
560             FrameMac* parentFrame = static_cast<FrameMac*>(tree()->parent());
561             NSView* parentView = parentFrame->d->m_view->getDocumentView();
562             [parentFrame->_bridge makeFirstResponder:parentView];
563         }
564     }
565     END_BLOCK_OBJC_EXCEPTIONS;
566 }
567
568 String FrameMac::advanceToNextMisspelling(bool startBeforeSelection)
569 {
570     int exception = 0;
571
572     // The basic approach is to search in two phases - from the selection end to the end of the doc, and
573     // then we wrap and search from the doc start to (approximately) where we started.
574     
575     // Start at the end of the selection, search to edge of document.  Starting at the selection end makes
576     // repeated "check spelling" commands work.
577     Selection selection(selectionController()->selection());
578     RefPtr<Range> searchRange(rangeOfContents(document()));
579     bool startedWithSelection = false;
580     if (selection.start().node()) {
581         startedWithSelection = true;
582         if (startBeforeSelection) {
583             VisiblePosition start(selection.visibleStart());
584             // We match AppKit's rule: Start 1 character before the selection.
585             VisiblePosition oneBeforeStart = start.previous();
586             setStart(searchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start);
587         } else
588             setStart(searchRange.get(), selection.visibleEnd());
589     }
590
591     // If we're not in an editable node, try to find one, make that our range to work in
592     Node *editableNode = searchRange->startContainer(exception);
593     if (!editableNode->isContentEditable()) {
594         editableNode = editableNode->nextEditable();
595         if (!editableNode) {
596             return String();
597         }
598         searchRange->setStartBefore(editableNode, exception);
599         startedWithSelection = false;   // won't need to wrap
600     }
601     
602     // topNode defines the whole range we want to operate on 
603     Node *topNode = editableNode->rootEditableElement();
604     searchRange->setEnd(topNode, maxDeepOffset(topNode), exception);
605
606     // Make sure start of searchRange is not in the middle of a word.  Jumping back a char and then
607     // forward by a word happens to do the trick.
608     if (startedWithSelection) {
609         VisiblePosition oneBeforeStart = startVisiblePosition(searchRange.get(), DOWNSTREAM).previous();
610         if (oneBeforeStart.isNotNull()) {
611             setStart(searchRange.get(), endOfWord(oneBeforeStart));
612         } // else we were already at the start of the editable node
613     }
614     
615     if (searchRange->collapsed(exception))
616         return String();       // nothing to search in
617     
618     // Get the spell checker if it is available
619     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
620     if (checker == nil)
621         return String();
622         
623     WordAwareIterator it(searchRange.get());
624     bool wrapped = false;
625     
626     // We go to the end of our first range instead of the start of it, just to be sure
627     // we don't get foiled by any word boundary problems at the start.  It means we might
628     // do a tiny bit more searching.
629     Node *searchEndAfterWrapNode = it.range()->endContainer(exception);
630     int searchEndAfterWrapOffset = it.range()->endOffset(exception);
631
632     while (1) {
633         if (!it.atEnd()) {      // we may be starting at the end of the doc, and already by atEnd
634             const UChar* chars = it.characters();
635             int len = it.length();
636             if (len > 1 || !DeprecatedChar(chars[0]).isSpace()) {
637                 NSString *chunk = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(chars) length:len freeWhenDone:NO];
638                 NSRange misspelling = [checker checkSpellingOfString:chunk startingAt:0 language:nil wrap:NO inSpellDocumentWithTag:editor()->client()->spellCheckerDocumentTag() wordCount:NULL];
639                 [chunk release];
640                 if (misspelling.length > 0) {
641                     // Build up result range and string.  Note the misspelling may span many text nodes,
642                     // but the CharIterator insulates us from this complexity
643                     RefPtr<Range> misspellingRange(rangeOfContents(document()));
644                     CharacterIterator chars(it.range().get());
645                     chars.advance(misspelling.location);
646                     misspellingRange->setStart(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
647                     DeprecatedString result = chars.string(misspelling.length);
648                     misspellingRange->setEnd(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
649
650                     selectionController()->setSelection(Selection(misspellingRange.get(), DOWNSTREAM));
651                     revealSelection();
652                     // Mark misspelling in document.
653                     document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
654                     return result;
655                 }
656             }
657         
658             it.advance();
659         }
660         if (it.atEnd()) {
661             if (wrapped || !startedWithSelection) {
662                 break;      // finished the second range, or we did the whole doc with the first range
663             } else {
664                 // we've gone from the selection to the end of doc, now wrap around
665                 wrapped = YES;
666                 searchRange->setStart(topNode, 0, exception);
667                 // going until the end of the very first chunk we tested is far enough
668                 searchRange->setEnd(searchEndAfterWrapNode, searchEndAfterWrapOffset, exception);
669                 it = WordAwareIterator(searchRange.get());
670             }
671         }   
672     }
673     
674     return String();
675 }
676
677 bool FrameMac::wheelEvent(NSEvent *event)
678 {
679     FrameView *v = d->m_view.get();
680
681     if (v) {
682         NSEvent *oldCurrentEvent = _currentEvent;
683         _currentEvent = HardRetain(event);
684
685         PlatformWheelEvent qEvent(event);
686         v->handleWheelEvent(qEvent);
687
688         ASSERT(_currentEvent == event);
689         HardRelease(event);
690         _currentEvent = oldCurrentEvent;
691
692         if (qEvent.isAccepted())
693             return true;
694     }
695     
696     return false;
697 }
698
699 void FrameMac::startRedirectionTimer()
700 {
701     stopRedirectionTimer();
702
703     Frame::startRedirectionTimer();
704
705     // Don't report history navigations, just actual redirection.
706     if (d->m_scheduledRedirection != historyNavigationScheduled) {
707         double fireDate = currentTime() + d->m_redirectionTimer.nextFireInterval();
708         loader()->clientRedirected(KURL(d->m_redirectURL).getNSURL(),
709             d->m_delayRedirect, fireDate, d->m_redirectLockHistory, d->m_executingJavaScriptFormAction);
710     }
711 }
712
713 void FrameMac::stopRedirectionTimer()
714 {
715     bool wasActive = d->m_redirectionTimer.isActive();
716
717     Frame::stopRedirectionTimer();
718
719     // Don't report history navigations, just actual redirection.
720     if (wasActive && d->m_scheduledRedirection != historyNavigationScheduled)
721         loader()->clientRedirectCancelledOrFinished(d->m_cancelWithLoadInProgress);
722 }
723
724 String FrameMac::userAgent() const
725 {
726     return loader()->client()->userAgent(url().getNSURL());
727 }
728
729 String FrameMac::mimeTypeForFileName(const String& fileName) const
730 {
731     BEGIN_BLOCK_OBJC_EXCEPTIONS;
732     return [_bridge MIMETypeForPath:fileName];
733     END_BLOCK_OBJC_EXCEPTIONS;
734
735     return String();
736 }
737
738 NSView* FrameMac::nextKeyViewInFrame(Node* n, SelectionDirection direction, bool* focusCallResultedInViewBeingCreated)
739 {
740     Document* doc = document();
741     if (!doc)
742         return nil;
743     
744     RefPtr<Node> node = n;
745     for (;;) {
746         node = direction == SelectingNext
747             ? doc->nextFocusNode(node.get()) : doc->previousFocusNode(node.get());
748         if (!node)
749             return nil;
750         
751         RenderObject* renderer = node->renderer();
752         
753         if (!renderer->isWidget()) {
754             static_cast<Element*>(node.get())->focus(); 
755             // The call to focus might have triggered event handlers that causes the 
756             // current renderer to be destroyed.
757             if (!(renderer = node->renderer()))
758                 continue;
759                 
760             // FIXME: When all input elements are native, we should investigate if this extra check is needed
761             if (!renderer->isWidget()) {
762                 [_bridge willMakeFirstResponderForNodeFocus];
763                 return [_bridge documentView];
764             } else if (focusCallResultedInViewBeingCreated)
765                 *focusCallResultedInViewBeingCreated = true;
766         }
767
768         if (Widget* widget = static_cast<RenderWidget*>(renderer)->widget()) {
769             NSView* view;
770             if (widget->isFrameView())
771                 view = Mac(static_cast<FrameView*>(widget)->frame())->nextKeyViewInFrame(0, direction);
772             else
773                 view = widget->getView();
774             if (view)
775                 return view;
776         }
777     }
778 }
779
780 NSView *FrameMac::nextKeyViewInFrameHierarchy(Node *node, SelectionDirection direction)
781 {
782     bool focusCallResultedInViewBeingCreated = false;
783     NSView *next = nextKeyViewInFrame(node, direction, &focusCallResultedInViewBeingCreated);
784     if (!next)
785         if (FrameMac *parent = Mac(tree()->parent()))
786             next = parent->nextKeyViewInFrameHierarchy(ownerElement(), direction);
787     
788     // remove focus from currently focused node if we're giving focus to another view
789     // unless the other view was created as a result of calling focus in nextKeyViewWithFrame.
790     // FIXME: The focusCallResultedInViewBeingCreated calls can be removed when all input element types
791     // have been made native.
792     if (next && (next != [_bridge documentView] && !focusCallResultedInViewBeingCreated))
793         if (Document *doc = document())
794             doc->setFocusNode(0);
795
796     // The common case where a view was created is when an <input> element changed from native 
797     // to non-native. When this happens, HTMLGenericFormElement::attach() method will call setFocus()
798     // on the widget. For views with a field editor, setFocus() will set the active responder to be the field editor. 
799     // In this case, we want to return the field editor as the next key view. Otherwise, the focus will be lost
800     // and a blur message will be sent. 
801     // FIXME: This code can be removed when all input element types are native.
802     if (focusCallResultedInViewBeingCreated) {
803         if ([[next window] firstResponder] == [[next window] fieldEditor:NO forObject:next])
804             return [[next window] fieldEditor:NO forObject:next];
805     }
806     
807     return next;
808 }
809
810 NSView *FrameMac::nextKeyView(Node *node, SelectionDirection direction)
811 {
812     NSView * next;
813     BEGIN_BLOCK_OBJC_EXCEPTIONS;
814
815     next = nextKeyViewInFrameHierarchy(node, direction);
816     if (next)
817         return next;
818
819     // Look at views from the top level part up, looking for a next key view that we can use.
820
821     next = direction == SelectingNext
822         ? [_bridge nextKeyViewOutsideWebFrameViews]
823         : [_bridge previousKeyViewOutsideWebFrameViews];
824
825     if (next)
826         return next;
827
828     END_BLOCK_OBJC_EXCEPTIONS;
829     
830     // If all else fails, make a loop by starting from 0.
831     return nextKeyViewInFrameHierarchy(0, direction);
832 }
833
834 NSView *FrameMac::nextKeyViewForWidget(Widget *startingWidget, SelectionDirection direction)
835 {
836     // Use the event filter object to figure out which RenderWidget owns this Widget and get to the DOM.
837     // Then get the next key view in the order determined by the DOM.
838     Node *node = nodeForWidget(startingWidget);
839     ASSERT(node);
840     return Mac(frameForNode(node))->nextKeyView(node, direction);
841 }
842
843 bool FrameMac::currentEventIsMouseDownInWidget(Widget *candidate)
844 {
845     BEGIN_BLOCK_OBJC_EXCEPTIONS;
846     switch ([[NSApp currentEvent] type]) {
847         case NSLeftMouseDown:
848         case NSRightMouseDown:
849         case NSOtherMouseDown:
850             break;
851         default:
852             return NO;
853     }
854     END_BLOCK_OBJC_EXCEPTIONS;
855     
856     Node *node = nodeForWidget(candidate);
857     ASSERT(node);
858     return frameForNode(node)->d->m_view->nodeUnderMouse() == node;
859 }
860
861 bool FrameMac::currentEventIsKeyboardOptionTab()
862 {
863     BEGIN_BLOCK_OBJC_EXCEPTIONS;
864     NSEvent *evt = [NSApp currentEvent];
865     if ([evt type] != NSKeyDown) {
866         return NO;
867     }
868
869     if (([evt modifierFlags] & NSAlternateKeyMask) == 0) {
870         return NO;
871     }
872     
873     NSString *chars = [evt charactersIgnoringModifiers];
874     if ([chars length] != 1)
875         return NO;
876     
877     const unichar tabKey = 0x0009;
878     const unichar shiftTabKey = 0x0019;
879     unichar c = [chars characterAtIndex:0];
880     if (c != tabKey && c != shiftTabKey)
881         return NO;
882     
883     END_BLOCK_OBJC_EXCEPTIONS;
884     return YES;
885 }
886
887 bool FrameMac::handleKeyboardOptionTabInView(NSView *view)
888 {
889     if (FrameMac::currentEventIsKeyboardOptionTab()) {
890         if (([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) != 0) {
891             [[view window] selectKeyViewPrecedingView:view];
892         } else {
893             [[view window] selectKeyViewFollowingView:view];
894         }
895         return YES;
896     }
897     
898     return NO;
899 }
900
901 bool FrameMac::tabsToLinks() const
902 {
903     if ([_bridge keyboardUIMode] & WebCoreKeyboardAccessTabsToLinks)
904         return !FrameMac::currentEventIsKeyboardOptionTab();
905     else
906         return FrameMac::currentEventIsKeyboardOptionTab();
907 }
908
909 bool FrameMac::tabsToAllControls() const
910 {
911     WebCoreKeyboardUIMode keyboardUIMode = [_bridge keyboardUIMode];
912     BOOL handlingOptionTab = FrameMac::currentEventIsKeyboardOptionTab();
913
914     // If tab-to-links is off, option-tab always highlights all controls
915     if ((keyboardUIMode & WebCoreKeyboardAccessTabsToLinks) == 0 && handlingOptionTab) {
916         return YES;
917     }
918     
919     // If system preferences say to include all controls, we always include all controls
920     if (keyboardUIMode & WebCoreKeyboardAccessFull) {
921         return YES;
922     }
923     
924     // Otherwise tab-to-links includes all controls, unless the sense is flipped via option-tab.
925     if (keyboardUIMode & WebCoreKeyboardAccessTabsToLinks) {
926         return !handlingOptionTab;
927     }
928     
929     return handlingOptionTab;
930 }
931
932 KJS::Bindings::RootObject *FrameMac::executionContextForDOM()
933 {
934     if (!javaScriptEnabled())
935         return 0;
936
937     return bindingRootObject();
938 }
939
940 KJS::Bindings::RootObject *FrameMac::bindingRootObject()
941 {
942     assert(javaScriptEnabled());
943     if (!_bindingRoot) {
944         JSLock lock;
945         _bindingRoot = new KJS::Bindings::RootObject(0);    // The root gets deleted by JavaScriptCore.
946         KJS::JSObject *win = KJS::Window::retrieveWindow(this);
947         _bindingRoot->setRootObjectImp (win);
948         _bindingRoot->setInterpreter(scriptProxy()->interpreter());
949         addPluginRootObject (_bindingRoot);
950     }
951     return _bindingRoot;
952 }
953
954 WebScriptObject *FrameMac::windowScriptObject()
955 {
956     if (!javaScriptEnabled())
957         return 0;
958
959     if (!_windowScriptObject) {
960         KJS::JSLock lock;
961         KJS::JSObject *win = KJS::Window::retrieveWindow(this);
962         _windowScriptObject = HardRetainWithNSRelease([[WebScriptObject alloc] _initWithJSObject:win originExecutionContext:bindingRootObject() executionContext:bindingRootObject()]);
963     }
964
965     return _windowScriptObject;
966 }
967
968 NPObject *FrameMac::windowScriptNPObject()
969 {
970     if (!_windowScriptNPObject) {
971         if (javaScriptEnabled()) {
972             // JavaScript is enabled, so there is a JavaScript window object.  Return an NPObject bound to the window
973             // object.
974             KJS::JSObject *win = KJS::Window::retrieveWindow(this);
975             assert(win);
976             _windowScriptNPObject = _NPN_CreateScriptObject(0, win, bindingRootObject(), bindingRootObject());
977         } else {
978             // JavaScript is not enabled, so we cannot bind the NPObject to the JavaScript window object.
979             // Instead, we create an NPObject of a different class, one which is not bound to a JavaScript object.
980             _windowScriptNPObject = _NPN_CreateNoScriptObject();
981         }
982     }
983
984     return _windowScriptNPObject;
985 }
986
987 Widget* FrameMac::createJavaAppletWidget(const IntSize& size, Element* element, const HashMap<String, String>& args)
988 {
989     Widget* result = new Widget;
990     
991     BEGIN_BLOCK_OBJC_EXCEPTIONS;
992     
993     NSMutableArray *attributeNames = [[NSMutableArray alloc] init];
994     NSMutableArray *attributeValues = [[NSMutableArray alloc] init];
995     
996     DeprecatedString baseURLString;
997     HashMap<String, String>::const_iterator end = args.end();
998     for (HashMap<String, String>::const_iterator it = args.begin(); it != end; ++it) {
999         if (it->first.lower() == "baseurl")
1000             baseURLString = it->second.deprecatedString();
1001         [attributeNames addObject:it->first];
1002         [attributeValues addObject:it->second];
1003     }
1004     
1005     if (baseURLString.isEmpty())
1006         baseURLString = document()->baseURL();
1007
1008     result->setView([_bridge viewForJavaAppletWithFrame:NSMakeRect(0, 0, size.width(), size.height())
1009                                          attributeNames:attributeNames
1010                                         attributeValues:attributeValues
1011                                                 baseURL:completeURL(baseURLString).getNSURL()
1012                                              DOMElement:[DOMElement _elementWith:element]]);
1013     [attributeNames release];
1014     [attributeValues release];
1015     view()->addChild(result);
1016     
1017     END_BLOCK_OBJC_EXCEPTIONS;
1018     
1019     return result;
1020 }
1021
1022 void FrameMac::partClearedInBegin()
1023 {
1024     if (javaScriptEnabled())
1025         [_bridge windowObjectCleared];
1026 }
1027
1028 WebCoreFrameBridge *FrameMac::bridgeForWidget(const Widget *widget)
1029 {
1030     ASSERT_ARG(widget, widget);
1031     
1032     FrameMac *frame = Mac(frameForWidget(widget));
1033     ASSERT(frame);
1034     return frame->_bridge;
1035 }
1036
1037 NSView *FrameMac::documentViewForNode(Node *node)
1038 {
1039     WebCoreFrameBridge *bridge = Mac(frameForNode(node))->_bridge;
1040     return [bridge documentView];
1041 }
1042
1043 void FrameMac::saveDocumentState()
1044 {
1045     // Do not save doc state if the page has a password field and a form that would be submitted
1046     // via https
1047     if (!(d->m_doc && d->m_doc->hasPasswordField() && d->m_doc->hasSecureForm())) {
1048         BEGIN_BLOCK_OBJC_EXCEPTIONS;
1049         [_bridge saveDocumentState];
1050         END_BLOCK_OBJC_EXCEPTIONS;
1051     }
1052 }
1053
1054 void FrameMac::restoreDocumentState()
1055 {
1056     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1057     [_bridge restoreDocumentState];
1058     END_BLOCK_OBJC_EXCEPTIONS;
1059 }
1060
1061 void FrameMac::runJavaScriptAlert(const String& message)
1062 {
1063     String text = message;
1064     text.replace('\\', backslashAsCurrencySymbol());
1065     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1066     [_bridge runJavaScriptAlertPanelWithMessage:text];
1067     END_BLOCK_OBJC_EXCEPTIONS;
1068 }
1069
1070 bool FrameMac::runJavaScriptConfirm(const String& message)
1071 {
1072     String text = message;
1073     text.replace('\\', backslashAsCurrencySymbol());
1074
1075     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1076     return [_bridge runJavaScriptConfirmPanelWithMessage:text];
1077     END_BLOCK_OBJC_EXCEPTIONS;
1078
1079     return false;
1080 }
1081
1082 bool FrameMac::runJavaScriptPrompt(const String& prompt, const String& defaultValue, String& result)
1083 {
1084     String promptText = prompt;
1085     promptText.replace('\\', backslashAsCurrencySymbol());
1086     String defaultValueText = defaultValue;
1087     defaultValueText.replace('\\', backslashAsCurrencySymbol());
1088
1089     bool ok;
1090     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1091     NSString *returnedText = nil;
1092
1093     ok = [_bridge runJavaScriptTextInputPanelWithPrompt:prompt
1094         defaultText:defaultValue returningText:&returnedText];
1095
1096     if (ok) {
1097         result = String(returnedText);
1098         result.replace(backslashAsCurrencySymbol(), '\\');
1099     }
1100
1101     return ok;
1102     END_BLOCK_OBJC_EXCEPTIONS;
1103     
1104     return false;
1105 }
1106
1107 bool FrameMac::shouldInterruptJavaScript()
1108 {
1109     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1110     return [_bridge shouldInterruptJavaScript];
1111     END_BLOCK_OBJC_EXCEPTIONS;
1112     
1113     return false;
1114 }
1115
1116 bool FrameMac::locationbarVisible()
1117 {
1118     return [_bridge areToolbarsVisible];
1119 }
1120
1121 bool FrameMac::menubarVisible()
1122 {
1123     // The menubar is always on in Mac OS X UI
1124     return true;
1125 }
1126
1127 bool FrameMac::personalbarVisible()
1128 {
1129     return [_bridge areToolbarsVisible];
1130 }
1131
1132 bool FrameMac::statusbarVisible()
1133 {
1134     return [_bridge isStatusbarVisible];
1135 }
1136
1137 bool FrameMac::toolbarVisible()
1138 {
1139     return [_bridge areToolbarsVisible];
1140 }
1141
1142 void FrameMac::addMessageToConsole(const String &message, unsigned lineNumber, const String &sourceURL)
1143 {
1144     NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
1145         (NSString *)message, @"message",
1146         [NSNumber numberWithInt: lineNumber], @"lineNumber",
1147         (NSString *)sourceURL, @"sourceURL",
1148         NULL];
1149     [_bridge addMessageToConsole:dictionary];
1150 }
1151
1152 void FrameMac::createEmptyDocument()
1153 {
1154     // Although it's not completely clear from the name of this function,
1155     // it does nothing if we already have a document, and just creates an
1156     // empty one if we have no document at all.
1157     if (!d->m_doc) {
1158         loader()->loadEmptyDocumentSynchronously();
1159         updateBaseURLForEmptyDocument();
1160     }
1161 }
1162
1163 bool FrameMac::keyEvent(NSEvent *event)
1164 {
1165     bool result;
1166     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1167
1168     ASSERT([event type] == NSKeyDown || [event type] == NSKeyUp);
1169
1170     // Check for cases where we are too early for events -- possible unmatched key up
1171     // from pressing return in the location bar.
1172     Document *doc = document();
1173     if (!doc) {
1174         return false;
1175     }
1176     Node *node = doc->focusNode();
1177     if (!node) {
1178         if (doc->isHTMLDocument())
1179             node = doc->body();
1180         else
1181             node = doc->documentElement();
1182         if (!node)
1183             return false;
1184     }
1185     
1186     if ([event type] == NSKeyDown) {
1187         prepareForUserAction();
1188     }
1189
1190     NSEvent *oldCurrentEvent = _currentEvent;
1191     _currentEvent = HardRetain(event);
1192
1193     PlatformKeyboardEvent qEvent(event);
1194     result = !EventTargetNodeCast(node)->dispatchKeyEvent(qEvent);
1195
1196     // We want to send both a down and a press for the initial key event.
1197     // To get KHTML to do this, we send a second KeyPress with "is repeat" set to true,
1198     // which causes it to send a press to the DOM.
1199     // That's not a great hack; it would be good to do this in a better way.
1200     if ([event type] == NSKeyDown && ![event isARepeat]) {
1201         PlatformKeyboardEvent repeatEvent(event, true);
1202         if (!EventTargetNodeCast(node)->dispatchKeyEvent(repeatEvent))
1203             result = true;
1204     }
1205
1206     ASSERT(_currentEvent == event);
1207     HardRelease(event);
1208     _currentEvent = oldCurrentEvent;
1209
1210     return result;
1211
1212     END_BLOCK_OBJC_EXCEPTIONS;
1213
1214     return false;
1215 }
1216
1217 void FrameMac::handleMousePressEvent(const MouseEventWithHitTestResults& event)
1218 {
1219     bool singleClick = [_currentEvent clickCount] <= 1;
1220
1221     // If we got the event back, that must mean it wasn't prevented,
1222     // so it's allowed to start a drag or selection.
1223     _mouseDownMayStartSelect = canMouseDownStartSelect(event.targetNode());
1224     
1225     // Careful that the drag starting logic stays in sync with eventMayStartDrag()
1226     setMouseDownMayStartDrag(singleClick);
1227
1228     d->m_mousePressNode = event.targetNode();
1229     
1230     if (!passWidgetMouseDownEventToWidget(event)) {
1231         // We don't do this at the start of mouse down handling (before calling into WebCore),
1232         // because we don't want to do it until we know we didn't hit a widget.
1233         NSView *view = d->m_view->getDocumentView();
1234
1235         if (singleClick) {
1236             BEGIN_BLOCK_OBJC_EXCEPTIONS;
1237             if ([_bridge firstResponder] != view) {
1238                 [_bridge makeFirstResponder:view];
1239             }
1240             END_BLOCK_OBJC_EXCEPTIONS;
1241         }
1242
1243         Frame::handleMousePressEvent(event);
1244     }
1245 }
1246
1247 bool FrameMac::passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults& event)
1248 {
1249     // Figure out which view to send the event to.
1250     RenderObject *target = event.targetNode() ? event.targetNode()->renderer() : 0;
1251     if (!target || !target->isWidget())
1252         return false;
1253     
1254     // Doubleclick events don't exist in Cocoa.  Since passWidgetMouseDownEventToWidget will
1255     // just pass _currentEvent down to the widget,  we don't want to call it for events that
1256     // don't correspond to Cocoa events.  The mousedown/ups will have already been passed on as
1257     // part of the pressed/released handling.
1258     return passMouseDownEventToWidget(static_cast<RenderWidget*>(target)->widget());
1259 }
1260
1261 bool FrameMac::passWidgetMouseDownEventToWidget(RenderWidget *renderWidget)
1262 {
1263     return passMouseDownEventToWidget(renderWidget->widget());
1264 }
1265
1266 bool FrameMac::passMouseDownEventToWidget(Widget* widget)
1267 {
1268     // FIXME: this method always returns true
1269
1270     if (!widget) {
1271         LOG_ERROR("hit a RenderWidget without a corresponding Widget, means a frame is half-constructed");
1272         return true;
1273     }
1274
1275     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1276     
1277     NSView *nodeView = widget->getView();
1278     ASSERT(nodeView);
1279     ASSERT([nodeView superview]);
1280     NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[_currentEvent locationInWindow] fromView:nil]];
1281     if (view == nil) {
1282         LOG_ERROR("KHTML says we hit a RenderWidget, but AppKit doesn't agree we hit the corresponding NSView");
1283         return true;
1284     }
1285     
1286     if ([_bridge firstResponder] == view) {
1287         // In the case where we just became first responder, we should send the mouseDown:
1288         // to the NSTextField, not the NSTextField's editor. This code makes sure that happens.
1289         // If we don't do this, we see a flash of selected text when clicking in a text field.
1290         // FIXME: This is the only caller of textViewWasFirstResponderAtMouseDownTime. When we
1291         // eliminate all use of NSTextField/NSTextView in form fields we can eliminate this code,
1292         // and textViewWasFirstResponderAtMouseDownTime:, and the instance variable WebHTMLView
1293         // keeps solely to support textViewWasFirstResponderAtMouseDownTime:.
1294         if ([view isKindOfClass:[NSTextView class]] && ![_bridge textViewWasFirstResponderAtMouseDownTime:(NSTextView *)view]) {
1295             NSView *superview = view;
1296             while (superview != nodeView) {
1297                 superview = [superview superview];
1298                 ASSERT(superview);
1299                 if ([superview isKindOfClass:[NSControl class]]) {
1300                     NSControl *control = static_cast<NSControl*>(superview);
1301                     if ([control currentEditor] == view) {
1302                         view = superview;
1303                     }
1304                     break;
1305                 }
1306             }
1307         }
1308     } else {
1309         // Normally [NSWindow sendEvent:] handles setting the first responder.
1310         // But in our case, the event was sent to the view representing the entire web page.
1311         if ([_currentEvent clickCount] <= 1 && [view acceptsFirstResponder] && [view needsPanelToBecomeKey]) {
1312             [_bridge makeFirstResponder:view];
1313         }
1314     }
1315
1316     // We need to "defer loading" while tracking the mouse, because tearing down the
1317     // page while an AppKit control is tracking the mouse can cause a crash.
1318     
1319     // FIXME: In theory, WebCore now tolerates tear-down while tracking the
1320     // mouse. We should confirm that, and then remove the deferrsLoading
1321     // hack entirely.
1322     
1323     bool wasDeferringLoading = page()->defersLoading();
1324     if (!wasDeferringLoading)
1325         page()->setDefersLoading(true);
1326
1327     ASSERT(!_sendingEventToSubview);
1328     _sendingEventToSubview = true;
1329     [view mouseDown:_currentEvent];
1330     _sendingEventToSubview = false;
1331     
1332     if (!wasDeferringLoading)
1333         page()->setDefersLoading(false);
1334
1335     // Remember which view we sent the event to, so we can direct the release event properly.
1336     _mouseDownView = view;
1337     _mouseDownWasInSubframe = false;
1338
1339     END_BLOCK_OBJC_EXCEPTIONS;
1340
1341     return true;
1342 }
1343
1344 bool FrameMac::lastEventIsMouseUp() const
1345 {
1346     // Many AK widgets run their own event loops and consume events while the mouse is down.
1347     // When they finish, currentEvent is the mouseUp that they exited on.  We need to update
1348     // the khtml state with this mouseUp, which khtml never saw.  This method lets us detect
1349     // that state.
1350
1351     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1352     NSEvent *currentEventAfterHandlingMouseDown = [NSApp currentEvent];
1353     if (_currentEvent != currentEventAfterHandlingMouseDown) {
1354         if ([currentEventAfterHandlingMouseDown type] == NSLeftMouseUp) {
1355             return true;
1356         }
1357     }
1358     END_BLOCK_OBJC_EXCEPTIONS;
1359
1360     return false;
1361 }
1362     
1363 // Note that this does the same kind of check as [target isDescendantOf:superview].
1364 // There are two differences: This is a lot slower because it has to walk the whole
1365 // tree, and this works in cases where the target has already been deallocated.
1366 static bool findViewInSubviews(NSView *superview, NSView *target)
1367 {
1368     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1369     NSEnumerator *e = [[superview subviews] objectEnumerator];
1370     NSView *subview;
1371     while ((subview = [e nextObject])) {
1372         if (subview == target || findViewInSubviews(subview, target)) {
1373             return true;
1374         }
1375     }
1376     END_BLOCK_OBJC_EXCEPTIONS;
1377     
1378     return false;
1379 }
1380
1381 NSView *FrameMac::mouseDownViewIfStillGood()
1382 {
1383     // Since we have no way of tracking the lifetime of _mouseDownView, we have to assume that
1384     // it could be deallocated already. We search for it in our subview tree; if we don't find
1385     // it, we set it to nil.
1386     NSView *mouseDownView = _mouseDownView;
1387     if (!mouseDownView) {
1388         return nil;
1389     }
1390     FrameView *topFrameView = d->m_view.get();
1391     NSView *topView = topFrameView ? topFrameView->getView() : nil;
1392     if (!topView || !findViewInSubviews(topView, mouseDownView)) {
1393         _mouseDownView = nil;
1394         return nil;
1395     }
1396     return mouseDownView;
1397 }
1398
1399 bool FrameMac::eventMayStartDrag(NSEvent *event) const
1400 {
1401     // This is a pre-flight check of whether the event might lead to a drag being started.  Be careful
1402     // that its logic needs to stay in sync with handleMouseMoveEvent() and the way we setMouseDownMayStartDrag
1403     // in handleMousePressEvent
1404     
1405     if ([event type] != NSLeftMouseDown || [event clickCount] != 1) {
1406         return false;
1407     }
1408     
1409     BOOL DHTMLFlag, UAFlag;
1410     [_bridge allowDHTMLDrag:&DHTMLFlag UADrag:&UAFlag];
1411     if (!DHTMLFlag && !UAFlag) {
1412         return false;
1413     }
1414
1415     NSPoint loc = [event locationInWindow];
1416     IntPoint mouseDownPos = d->m_view->windowToContents(IntPoint(loc));
1417     HitTestResult result(mouseDownPos, true, false);
1418     renderer()->layer()->hitTest(result);
1419     bool srcIsDHTML;
1420     return result.innerNode()->renderer()->draggableNode(DHTMLFlag, UAFlag, mouseDownPos.x(), mouseDownPos.y(), srcIsDHTML);
1421 }
1422
1423 // The link drag hysteresis is much larger than the others because there
1424 // needs to be enough space to cancel the link press without starting a link drag,
1425 // and because dragging links is rare.
1426 const float LinkDragHysteresis = 40.0;
1427 const float ImageDragHysteresis = 5.0;
1428 const float TextDragHysteresis = 3.0;
1429 const float GeneralDragHysteresis = 3.0;
1430 const float TextDragDelay = 0.15;
1431
1432 bool FrameMac::dragHysteresisExceeded(float dragLocationX, float dragLocationY) const
1433 {
1434     IntPoint dragViewportLocation((int)dragLocationX, (int)dragLocationY);
1435     IntPoint dragLocation = d->m_view->windowToContents(dragViewportLocation);
1436     IntSize delta = dragLocation - m_mouseDownPos;
1437     
1438     float threshold = GeneralDragHysteresis;
1439     if (_dragSrcIsImage)
1440         threshold = ImageDragHysteresis;
1441     else if (_dragSrcIsLink)
1442         threshold = LinkDragHysteresis;
1443     else if (_dragSrcInSelection)
1444         threshold = TextDragHysteresis;
1445
1446     return fabsf(delta.width()) >= threshold || fabsf(delta.height()) >= threshold;
1447 }
1448
1449 void FrameMac::handleMouseMoveEvent(const MouseEventWithHitTestResults& event)
1450 {
1451     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1452
1453     if ([_currentEvent type] == NSLeftMouseDragged) {
1454         NSView *view = mouseDownViewIfStillGood();
1455
1456         if (view) {
1457             _sendingEventToSubview = true;
1458             [view mouseDragged:_currentEvent];
1459             _sendingEventToSubview = false;
1460             return;
1461         }
1462
1463         // Careful that the drag starting logic stays in sync with eventMayStartDrag()
1464     
1465         if (mouseDownMayStartDrag() && !_dragSrc) {
1466             BOOL tempFlag1, tempFlag2;
1467             [_bridge allowDHTMLDrag:&tempFlag1 UADrag:&tempFlag2];
1468             _dragSrcMayBeDHTML = tempFlag1;
1469             _dragSrcMayBeUA = tempFlag2;
1470             if (!_dragSrcMayBeDHTML && !_dragSrcMayBeUA) {
1471                 setMouseDownMayStartDrag(false);     // no element is draggable
1472             }
1473         }
1474         
1475         if (mouseDownMayStartDrag() && !_dragSrc) {
1476             // try to find an element that wants to be dragged
1477             HitTestResult result(m_mouseDownPos, true, false);
1478             renderer()->layer()->hitTest(result);
1479             Node *node = result.innerNode();
1480             _dragSrc = (node && node->renderer()) ? node->renderer()->draggableNode(_dragSrcMayBeDHTML, _dragSrcMayBeUA, m_mouseDownPos.x(), m_mouseDownPos.y(), _dragSrcIsDHTML) : 0;
1481             if (!_dragSrc) {
1482                 setMouseDownMayStartDrag(false);     // no element is draggable
1483             } else {
1484                 // remember some facts about this source, while we have a HitTestResult handy
1485                 node = result.URLElement();
1486                 _dragSrcIsLink = node && node->isLink();
1487
1488                 node = result.innerNonSharedNode();
1489                 _dragSrcIsImage = node && node->renderer() && node->renderer()->isImage();
1490                 
1491                 _dragSrcInSelection = selectionController()->contains(m_mouseDownPos);
1492             }                
1493         }
1494         
1495         // For drags starting in the selection, the user must wait between the mousedown and mousedrag,
1496         // or else we bail on the dragging stuff and allow selection to occur
1497         if (mouseDownMayStartDrag() && _dragSrcInSelection && [_currentEvent timestamp] - _mouseDownTimestamp < TextDragDelay) {
1498             setMouseDownMayStartDrag(false);
1499             // ...but if this was the first click in the window, we don't even want to start selection
1500             if (_activationEventNumber == [_currentEvent eventNumber]) {
1501                 _mouseDownMayStartSelect = false;
1502             }
1503         }
1504
1505         if (mouseDownMayStartDrag()) {
1506             // We are starting a text/image/url drag, so the cursor should be an arrow
1507             d->m_view->setCursor(pointerCursor());
1508             
1509             NSPoint dragLocation = [_currentEvent locationInWindow];
1510             if (dragHysteresisExceeded(dragLocation.x, dragLocation.y)) {
1511                 
1512                 // Once we're past the hysteresis point, we don't want to treat this gesture as a click
1513                 d->m_view->invalidateClick();
1514
1515                 NSImage *dragImage = nil;       // we use these values if WC is out of the loop
1516                 NSPoint dragLoc = NSZeroPoint;
1517                 NSDragOperation srcOp = NSDragOperationNone;                
1518                 BOOL wcWrotePasteboard = NO;
1519                 if (_dragSrcMayBeDHTML) {
1520                     NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
1521                     // Must be done before ondragstart adds types and data to the pboard,
1522                     // also done for security, as it erases data from the last drag
1523                     [pasteboard declareTypes:[NSArray array] owner:nil];
1524                     
1525                     freeClipboard();    // would only happen if we missed a dragEnd.  Do it anyway, just
1526                                         // to make sure it gets numbified
1527                     _dragClipboard = new ClipboardMac(true, pasteboard, ClipboardWritable, this);
1528                     
1529                     // If this is drag of an element, get set up to generate a default image.  Otherwise
1530                     // WebKit will generate the default, the element doesn't override.
1531                     if (_dragSrcIsDHTML) {
1532                         int srcX, srcY;
1533                         _dragSrc->renderer()->absolutePosition(srcX, srcY);
1534                         IntSize delta = m_mouseDownPos - IntPoint(srcX, srcY);
1535                         _dragClipboard->setDragImageElement(_dragSrc.get(), IntPoint() + delta);
1536                     } 
1537
1538                     setMouseDownMayStartDrag(dispatchDragSrcEvent(dragstartEvent, m_mouseDown) && mayCopy());
1539                     // Invalidate clipboard here against anymore pasteboard writing for security.  The drag
1540                     // image can still be changed as we drag, but not the pasteboard data.
1541                     _dragClipboard->setAccessPolicy(ClipboardImageWritable);
1542                     
1543                     if (mouseDownMayStartDrag()) {
1544                         // gather values from DHTML element, if it set any
1545                         _dragClipboard->sourceOperation(srcOp);
1546
1547                         NSArray *types = [pasteboard types];
1548                         wcWrotePasteboard = types && [types count] > 0;
1549
1550                         if (_dragSrcMayBeDHTML)
1551                             dragImage = _dragClipboard->dragNSImage(dragLoc);
1552                         
1553                         // Yuck, dragSourceMovedTo() can be called as a result of kicking off the drag with
1554                         // dragImage!  Because of that dumb reentrancy, we may think we've not started the
1555                         // drag when that happens.  So we have to assume it's started before we kick it off.
1556                         _dragClipboard->setDragHasStarted();
1557                     }
1558                 }
1559                 
1560                 if (mouseDownMayStartDrag()) {
1561                     BOOL startedDrag = [_bridge startDraggingImage:dragImage at:dragLoc operation:srcOp event:_currentEvent sourceIsDHTML:_dragSrcIsDHTML DHTMLWroteData:wcWrotePasteboard];
1562                     if (!startedDrag && _dragSrcMayBeDHTML) {
1563                         // WebKit canned the drag at the last minute - we owe _dragSrc a DRAGEND event
1564                         PlatformMouseEvent event(PlatformMouseEvent::currentEvent);
1565                         dispatchDragSrcEvent(dragendEvent, event);
1566                         setMouseDownMayStartDrag(false);
1567                     }
1568                 } 
1569
1570                 if (!mouseDownMayStartDrag()) {
1571                     // something failed to start the drag, cleanup
1572                     freeClipboard();
1573                     _dragSrc = 0;
1574                 }
1575             }
1576
1577             // No more default handling (like selection), whether we're past the hysteresis bounds or not
1578             return;
1579         }
1580         if (!mouseDownMayStartSelect() && !mouseDownMayStartAutoscroll())
1581             return;
1582             
1583     } else {
1584         // If we allowed the other side of the bridge to handle a drag
1585         // last time, then m_bMousePressed might still be set. So we
1586         // clear it now to make sure the next move after a drag
1587         // doesn't look like a drag.
1588         d->m_bMousePressed = false;
1589     }
1590
1591     Frame::handleMouseMoveEvent(event);
1592
1593     END_BLOCK_OBJC_EXCEPTIONS;
1594 }
1595
1596 // Returns whether caller should continue with "the default processing", which is the same as 
1597 // the event handler NOT setting the return value to false
1598 bool FrameMac::dispatchCPPEvent(const AtomicString &eventType, ClipboardAccessPolicy policy)
1599 {
1600     Node* target = selectionController()->start().element();
1601     if (!target && document())
1602         target = document()->body();
1603     if (!target)
1604         return true;
1605     if (target->isShadowNode())
1606         target = target->shadowParentNode();
1607     
1608     RefPtr<ClipboardMac> clipboard = new ClipboardMac(false, [NSPasteboard generalPasteboard], (ClipboardAccessPolicy)policy);
1609
1610     ExceptionCode ec = 0;
1611     RefPtr<Event> evt = new ClipboardEvent(eventType, true, true, clipboard.get());
1612     EventTargetNodeCast(target)->dispatchEvent(evt, ec, true);
1613     bool noDefaultProcessing = evt->defaultPrevented();
1614
1615     // invalidate clipboard here for security
1616     clipboard->setAccessPolicy(ClipboardNumb);
1617
1618     return !noDefaultProcessing;
1619 }
1620
1621 // WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items.  They
1622 // also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items.
1623 // We need to use onbeforecopy as a real menu enabler because we allow elements that are not
1624 // normally selectable to implement copy/paste (like divs, or a document body).
1625
1626 bool FrameMac::mayDHTMLCut()
1627 {
1628     return mayCopy() && !dispatchCPPEvent(beforecutEvent, ClipboardNumb);
1629 }
1630
1631 bool FrameMac::mayDHTMLCopy()
1632 {
1633     return mayCopy() && !dispatchCPPEvent(beforecopyEvent, ClipboardNumb);
1634 }
1635
1636 bool FrameMac::mayDHTMLPaste()
1637 {
1638     return !dispatchCPPEvent(beforepasteEvent, ClipboardNumb);
1639 }
1640
1641 bool FrameMac::tryDHTMLCut()
1642 {
1643     if (!mayCopy())
1644         return false;
1645
1646     // Must be done before oncut adds types and data to the pboard,
1647     // also done for security, as it erases data from the last copy/paste.
1648     [[NSPasteboard generalPasteboard] declareTypes:[NSArray array] owner:nil];
1649
1650     return !dispatchCPPEvent(cutEvent, ClipboardWritable);
1651 }
1652
1653 bool FrameMac::tryDHTMLCopy()
1654 {
1655     if (!mayCopy())
1656         return false;
1657
1658     // Must be done before oncopy adds types and data to the pboard,
1659     // also done for security, as it erases data from the last copy/paste.
1660     [[NSPasteboard generalPasteboard] declareTypes:[NSArray array] owner:nil];
1661
1662     return !dispatchCPPEvent(copyEvent, ClipboardWritable);
1663 }
1664
1665 bool FrameMac::tryDHTMLPaste()
1666 {
1667     return !dispatchCPPEvent(pasteEvent, ClipboardReadable);
1668 }
1669
1670 void FrameMac::handleMouseReleaseEvent(const MouseEventWithHitTestResults& event)
1671 {
1672     NSView *view = mouseDownViewIfStillGood();
1673     if (!view) {
1674         // If this was the first click in the window, we don't even want to clear the selection.
1675         // This case occurs when the user clicks on a draggable element, since we have to process
1676         // the mouse down and drag events to see if we might start a drag.  For other first clicks
1677         // in a window, we just don't acceptFirstMouse, and the whole down-drag-up sequence gets
1678         // ignored upstream of this layer.
1679         if (_activationEventNumber != [_currentEvent eventNumber])
1680             Frame::handleMouseReleaseEvent(event);
1681         return;
1682     }
1683     stopAutoscrollTimer();
1684     
1685     _sendingEventToSubview = true;
1686     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1687     [view mouseUp:_currentEvent];
1688     END_BLOCK_OBJC_EXCEPTIONS;
1689     _sendingEventToSubview = false;
1690 }
1691
1692 bool FrameMac::passSubframeEventToSubframe(MouseEventWithHitTestResults& event, Frame* subframe)
1693 {
1694     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1695
1696     switch ([_currentEvent type]) {
1697         case NSMouseMoved:
1698             Mac(subframe)->mouseMoved(_currentEvent);
1699             return true;
1700         
1701         case NSLeftMouseDown: {
1702             Node* node = event.targetNode();
1703             if (!node)
1704                 return false;
1705             RenderObject *renderer = node->renderer();
1706             if (!renderer || !renderer->isWidget())
1707                 return false;
1708             Widget *widget = static_cast<RenderWidget*>(renderer)->widget();
1709             if (!widget || !widget->isFrameView())
1710                 return false;
1711             if (!passWidgetMouseDownEventToWidget(static_cast<RenderWidget*>(renderer)))
1712                 return false;
1713             _mouseDownWasInSubframe = true;
1714             return true;
1715         }
1716         case NSLeftMouseUp: {
1717             if (!_mouseDownWasInSubframe)
1718                 return false;
1719             NSView *view = mouseDownViewIfStillGood();
1720             if (!view)
1721                 return false;
1722             ASSERT(!_sendingEventToSubview);
1723             _sendingEventToSubview = true;
1724             [view mouseUp:_currentEvent];
1725             _sendingEventToSubview = false;
1726             return true;
1727         }
1728         case NSLeftMouseDragged: {
1729             if (!_mouseDownWasInSubframe)
1730                 return false;
1731             NSView *view = mouseDownViewIfStillGood();
1732             if (!view)
1733                 return false;
1734             ASSERT(!_sendingEventToSubview);
1735             _sendingEventToSubview = true;
1736             [view mouseDragged:_currentEvent];
1737             _sendingEventToSubview = false;
1738             return true;
1739         }
1740         default:
1741             return false;
1742     }
1743     END_BLOCK_OBJC_EXCEPTIONS;
1744
1745     return false;
1746 }
1747
1748 bool FrameMac::passWheelEventToWidget(Widget* widget)
1749 {
1750     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1751         
1752     if ([_currentEvent type] != NSScrollWheel || _sendingEventToSubview || !widget) 
1753         return false;
1754     else {
1755         NSView *nodeView = widget->getView();
1756         ASSERT(nodeView);
1757         ASSERT([nodeView superview]);
1758         NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[_currentEvent locationInWindow] fromView:nil]];
1759     
1760         ASSERT(view);
1761         _sendingEventToSubview = true;
1762         [view scrollWheel:_currentEvent];
1763         _sendingEventToSubview = false;
1764         return true;
1765     }
1766             
1767     END_BLOCK_OBJC_EXCEPTIONS;
1768     return false;
1769 }
1770
1771 void FrameMac::mouseDown(NSEvent *event)
1772 {
1773     FrameView *v = d->m_view.get();
1774     if (!v || _sendingEventToSubview)
1775         return;
1776
1777     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1778
1779     prepareForUserAction();
1780
1781     _mouseDownView = nil;
1782     _dragSrc = 0;
1783     
1784     NSEvent *oldCurrentEvent = _currentEvent;
1785     _currentEvent = HardRetain(event);
1786     m_mouseDown = PlatformMouseEvent(event);
1787     NSPoint loc = [event locationInWindow];
1788     m_mouseDownPos = d->m_view->windowToContents(IntPoint(loc));
1789     _mouseDownTimestamp = [event timestamp];
1790
1791     setMouseDownMayStartDrag(false);
1792     _mouseDownMayStartSelect = false;
1793     setMouseDownMayStartAutoscroll(false);
1794     
1795     v->handleMousePressEvent(event);
1796     
1797     ASSERT(_currentEvent == event);
1798     HardRelease(event);
1799     _currentEvent = oldCurrentEvent;
1800
1801     END_BLOCK_OBJC_EXCEPTIONS;
1802 }
1803
1804 void FrameMac::mouseDragged(NSEvent *event)
1805 {
1806     FrameView *v = d->m_view.get();
1807     if (!v || _sendingEventToSubview) {
1808         return;
1809     }
1810
1811     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1812
1813     NSEvent *oldCurrentEvent = _currentEvent;
1814     _currentEvent = HardRetain(event);
1815
1816     v->handleMouseMoveEvent(event);
1817     
1818     ASSERT(_currentEvent == event);
1819     HardRelease(event);
1820     _currentEvent = oldCurrentEvent;
1821
1822     END_BLOCK_OBJC_EXCEPTIONS;
1823 }
1824
1825 void FrameMac::mouseUp(NSEvent *event)
1826 {
1827     FrameView *v = d->m_view.get();
1828     if (!v || _sendingEventToSubview)
1829         return;
1830
1831     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1832
1833     NSEvent *oldCurrentEvent = _currentEvent;
1834     _currentEvent = HardRetain(event);
1835
1836     // Our behavior here is a little different that Qt. Qt always sends
1837     // a mouse release event, even for a double click. To correct problems
1838     // in khtml's DOM click event handling we do not send a release here
1839     // for a double click. Instead we send that event from FrameView's
1840     // handleMouseDoubleClickEvent. Note also that the third click of
1841     // a triple click is treated as a single click, but the fourth is then
1842     // treated as another double click. Hence the "% 2" below.
1843     int clickCount = [event clickCount];
1844     if (clickCount > 0 && clickCount % 2 == 0)
1845         v->handleMouseDoubleClickEvent(event);
1846     else
1847         v->handleMouseReleaseEvent(event);
1848     
1849     ASSERT(_currentEvent == event);
1850     HardRelease(event);
1851     _currentEvent = oldCurrentEvent;
1852     
1853     _mouseDownView = nil;
1854
1855     END_BLOCK_OBJC_EXCEPTIONS;
1856 }
1857
1858 /*
1859  A hack for the benefit of AK's PopUpButton, which uses the Carbon menu manager, which thus
1860  eats all subsequent events after it is starts its modal tracking loop.  After the interaction
1861  is done, this routine is used to fix things up.  When a mouse down started us tracking in
1862  the widget, we post a fake mouse up to balance the mouse down we started with. When a 
1863  key down started us tracking in the widget, we post a fake key up to balance things out.
1864  In addition, we post a fake mouseMoved to get the cursor in sync with whatever we happen to 
1865  be over after the tracking is done.
1866  */
1867 void FrameMac::sendFakeEventsAfterWidgetTracking(NSEvent *initiatingEvent)
1868 {
1869     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1870
1871     _sendingEventToSubview = false;
1872     int eventType = [initiatingEvent type];
1873     if (eventType == NSLeftMouseDown || eventType == NSKeyDown) {
1874         NSEvent *fakeEvent = nil;
1875         if (eventType == NSLeftMouseDown) {
1876             fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
1877                                     location:[initiatingEvent locationInWindow]
1878                                 modifierFlags:[initiatingEvent modifierFlags]
1879                                     timestamp:[initiatingEvent timestamp]
1880                                 windowNumber:[initiatingEvent windowNumber]
1881                                         context:[initiatingEvent context]
1882                                     eventNumber:[initiatingEvent eventNumber]
1883                                     clickCount:[initiatingEvent clickCount]
1884                                     pressure:[initiatingEvent pressure]];
1885         
1886             mouseUp(fakeEvent);
1887         }
1888         else { // eventType == NSKeyDown
1889             fakeEvent = [NSEvent keyEventWithType:NSKeyUp
1890                                     location:[initiatingEvent locationInWindow]
1891                                modifierFlags:[initiatingEvent modifierFlags]
1892                                    timestamp:[initiatingEvent timestamp]
1893                                 windowNumber:[initiatingEvent windowNumber]
1894                                      context:[initiatingEvent context]
1895                                   characters:[initiatingEvent characters] 
1896                  charactersIgnoringModifiers:[initiatingEvent charactersIgnoringModifiers] 
1897                                    isARepeat:[initiatingEvent isARepeat] 
1898                                      keyCode:[initiatingEvent keyCode]];
1899             keyEvent(fakeEvent);
1900         }
1901         // FIXME:  We should really get the current modifierFlags here, but there's no way to poll
1902         // them in Cocoa, and because the event stream was stolen by the Carbon menu code we have
1903         // no up-to-date cache of them anywhere.
1904         fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
1905                                        location:[[_bridge window] convertScreenToBase:[NSEvent mouseLocation]]
1906                                   modifierFlags:[initiatingEvent modifierFlags]
1907                                       timestamp:[initiatingEvent timestamp]
1908                                    windowNumber:[initiatingEvent windowNumber]
1909                                         context:[initiatingEvent context]
1910                                     eventNumber:0
1911                                      clickCount:0
1912                                        pressure:0];
1913         mouseMoved(fakeEvent);
1914     }
1915     
1916     END_BLOCK_OBJC_EXCEPTIONS;
1917 }
1918
1919 void FrameMac::mouseMoved(NSEvent *event)
1920 {
1921     FrameView *v = d->m_view.get();
1922     // Reject a mouse moved if the button is down - screws up tracking during autoscroll
1923     // These happen because WebKit sometimes has to fake up moved events.
1924     if (!v || d->m_bMousePressed || _sendingEventToSubview)
1925         return;
1926     
1927     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1928
1929     NSEvent *oldCurrentEvent = _currentEvent;
1930     _currentEvent = HardRetain(event);
1931     
1932     v->handleMouseMoveEvent(event);
1933     
1934     ASSERT(_currentEvent == event);
1935     HardRelease(event);
1936     _currentEvent = oldCurrentEvent;
1937
1938     END_BLOCK_OBJC_EXCEPTIONS;
1939 }
1940
1941 // Called as we walk up the element chain for nodes with CSS property -webkit-user-drag == auto
1942 bool FrameMac::shouldDragAutoNode(Node* node, const IntPoint& point) const
1943 {
1944     // We assume that WebKit only cares about dragging things that can be leaf nodes (text, images, urls).
1945     // This saves a bunch of expensive calls (creating WC and WK element dicts) as we walk farther up
1946     // the node hierarchy, and we also don't have to cook up a way to ask WK about non-leaf nodes
1947     // (since right now WK just hit-tests using a cached lastMouseDown).
1948     if (!node->hasChildNodes() && d->m_view) {
1949         NSPoint eventLoc = d->m_view->contentsToWindow(point);
1950         return [_bridge mayStartDragAtEventLocation:eventLoc];
1951     } else
1952         return NO;
1953 }
1954
1955 bool FrameMac::sendContextMenuEvent(NSEvent *event)
1956 {
1957     Document* doc = d->m_doc.get();
1958     FrameView* v = d->m_view.get();
1959     if (!doc || !v)
1960         return false;
1961
1962     bool swallowEvent;
1963     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1964
1965     NSEvent *oldCurrentEvent = _currentEvent;
1966     _currentEvent = HardRetain(event);
1967     
1968     PlatformMouseEvent mouseEvent(event);
1969
1970     IntPoint viewportPos = v->windowToContents(mouseEvent.pos());
1971     MouseEventWithHitTestResults mev = doc->prepareMouseEvent(false, true, false, viewportPos, mouseEvent);
1972
1973     swallowEvent = v->dispatchMouseEvent(contextmenuEvent, mev.targetNode(), true, 0, mouseEvent, true);
1974     if (!swallowEvent && !selectionController()->contains(viewportPos) &&
1975             ([_bridge selectWordBeforeMenuEvent] || [_bridge isEditable]
1976                 || (mev.targetNode() && mev.targetNode()->isContentEditable()))) {
1977         _mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection
1978         selectClosestWordFromMouseEvent(mouseEvent, mev.targetNode());
1979     }
1980
1981     ASSERT(_currentEvent == event);
1982     HardRelease(event);
1983     _currentEvent = oldCurrentEvent;
1984
1985     return swallowEvent;
1986
1987     END_BLOCK_OBJC_EXCEPTIONS;
1988
1989     return false;
1990 }
1991
1992 NSImage *FrameMac::imageFromRect(NSRect rect) const
1993 {
1994     NSView *view = d->m_view->getDocumentView();
1995     if (!view)
1996         return nil;
1997     
1998     NSImage *resultImage;
1999     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2000     
2001     NSRect bounds = [view bounds];
2002     
2003     // Round image rect size in window coordinate space to avoid pixel cracks at HiDPI (4622794)
2004     rect = [view convertRect:rect toView:nil];
2005     rect.size.height = roundf(rect.size.height);
2006     rect.size.width = roundf(rect.size.width);
2007     rect = [view convertRect:rect fromView:nil];
2008     
2009     resultImage = [[[NSImage alloc] initWithSize:rect.size] autorelease];
2010
2011     if (rect.size.width != 0 && rect.size.height != 0) {
2012         [resultImage setFlipped:YES];
2013         [resultImage lockFocus];
2014
2015         CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
2016
2017         CGContextSaveGState(context);
2018         CGContextTranslateCTM(context, bounds.origin.x - rect.origin.x, bounds.origin.y - rect.origin.y);
2019         [view drawRect:rect];
2020         CGContextRestoreGState(context);
2021
2022         [resultImage unlockFocus];
2023         [resultImage setFlipped:NO];
2024     }
2025
2026     return resultImage;
2027
2028     END_BLOCK_OBJC_EXCEPTIONS;
2029     
2030     return nil;
2031 }
2032
2033 NSImage* FrameMac::selectionImage(bool forceWhiteText) const
2034 {
2035     d->m_paintRestriction = forceWhiteText ? PaintRestrictionSelectionOnlyWhiteText : PaintRestrictionSelectionOnly;
2036     NSImage *result = imageFromRect(visibleSelectionRect());
2037     d->m_paintRestriction = PaintRestrictionNone;
2038     return result;
2039 }
2040
2041 NSImage *FrameMac::snapshotDragImage(Node *node, NSRect *imageRect, NSRect *elementRect) const
2042 {
2043     RenderObject *renderer = node->renderer();
2044     if (!renderer)
2045         return nil;
2046     
2047     renderer->updateDragState(true);    // mark dragged nodes (so they pick up the right CSS)
2048     d->m_doc->updateLayout();        // forces style recalc - needed since changing the drag state might
2049                                         // imply new styles, plus JS could have changed other things
2050     IntRect topLevelRect;
2051     NSRect paintingRect = renderer->paintingRootRect(topLevelRect);
2052
2053     d->m_elementToDraw = node;              // invoke special sub-tree drawing mode
2054     NSImage *result = imageFromRect(paintingRect);
2055     renderer->updateDragState(false);
2056     d->m_doc->updateLayout();
2057     d->m_elementToDraw = 0;
2058
2059     if (elementRect)
2060         *elementRect = topLevelRect;
2061     if (imageRect)
2062         *imageRect = paintingRect;
2063     return result;
2064 }
2065
2066 NSFont *FrameMac::fontForSelection(bool *hasMultipleFonts) const
2067 {
2068     if (hasMultipleFonts)
2069         *hasMultipleFonts = false;
2070
2071     if (!selectionController()->isRange()) {
2072         Node *nodeToRemove;
2073         RenderStyle *style = styleForSelectionStart(nodeToRemove); // sets nodeToRemove
2074
2075         NSFont *result = 0;
2076         if (style)
2077             result = style->font().primaryFont()->getNSFont();
2078         
2079         if (nodeToRemove) {
2080             ExceptionCode ec;
2081             nodeToRemove->remove(ec);
2082             ASSERT(ec == 0);
2083         }
2084
2085         return result;
2086     }
2087
2088     NSFont *font = nil;
2089
2090     RefPtr<Range> range = selectionController()->toRange();
2091     Node *startNode = range->editingStartPosition().node();
2092     if (startNode != nil) {
2093         Node *pastEnd = range->pastEndNode();
2094         // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one
2095         // unreproducible case where this didn't happen, so check for nil also.
2096         for (Node *n = startNode; n && n != pastEnd; n = n->traverseNextNode()) {
2097             RenderObject *renderer = n->renderer();
2098             if (!renderer)
2099                 continue;
2100             // FIXME: Are there any node types that have renderers, but that we should be skipping?
2101             NSFont *f = renderer->style()->font().primaryFont()->getNSFont();
2102             if (!font) {
2103                 font = f;
2104                 if (!hasMultipleFonts)
2105                     break;
2106             } else if (font != f) {
2107                 *hasMultipleFonts = true;
2108                 break;
2109             }
2110         }
2111     }
2112
2113     return font;
2114 }
2115
2116 NSDictionary *FrameMac::fontAttributesForSelectionStart() const
2117 {
2118     Node *nodeToRemove;
2119     RenderStyle *style = styleForSelectionStart(nodeToRemove);
2120     if (!style)
2121         return nil;
2122
2123     NSMutableDictionary *result = [NSMutableDictionary dictionary];
2124
2125     if (style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0)
2126         [result setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2127
2128     if (style->font().primaryFont()->getNSFont())
2129         [result setObject:style->font().primaryFont()->getNSFont() forKey:NSFontAttributeName];
2130
2131     if (style->color().isValid() && style->color() != Color::black)
2132         [result setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2133
2134     ShadowData *shadow = style->textShadow();
2135     if (shadow) {
2136         NSShadow *s = [[NSShadow alloc] init];
2137         [s setShadowOffset:NSMakeSize(shadow->x, shadow->y)];
2138         [s setShadowBlurRadius:shadow->blur];
2139         [s setShadowColor:nsColor(shadow->color)];
2140         [result setObject:s forKey:NSShadowAttributeName];
2141     }
2142
2143     int decoration = style->textDecorationsInEffect();
2144     if (decoration & LINE_THROUGH)
2145         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSStrikethroughStyleAttributeName];
2146
2147     int superscriptInt = 0;
2148     switch (style->verticalAlign()) {
2149         case BASELINE:
2150         case BOTTOM:
2151         case BASELINE_MIDDLE:
2152         case LENGTH:
2153         case MIDDLE:
2154         case TEXT_BOTTOM:
2155         case TEXT_TOP:
2156         case TOP:
2157             break;
2158         case SUB:
2159             superscriptInt = -1;
2160             break;
2161         case SUPER:
2162             superscriptInt = 1;
2163             break;
2164     }
2165     if (superscriptInt)
2166         [result setObject:[NSNumber numberWithInt:superscriptInt] forKey:NSSuperscriptAttributeName];
2167
2168     if (decoration & UNDERLINE)
2169         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
2170
2171     if (nodeToRemove) {
2172         ExceptionCode ec = 0;
2173         nodeToRemove->remove(ec);
2174         ASSERT(ec == 0);
2175     }
2176
2177     return result;
2178 }
2179
2180 NSWritingDirection FrameMac::baseWritingDirectionForSelectionStart() const
2181 {
2182     NSWritingDirection result = NSWritingDirectionLeftToRight;
2183
2184     Position pos = selectionController()->selection().visibleStart().deepEquivalent();
2185     Node *node = pos.node();
2186     if (!node || !node->renderer() || !node->renderer()->containingBlock())
2187         return result;
2188     RenderStyle *style = node->renderer()->containingBlock()->style();
2189     if (!style)
2190         return result;
2191         
2192     switch (style->direction()) {
2193         case LTR:
2194             result = NSWritingDirectionLeftToRight;
2195             break;
2196         case RTL:
2197             result = NSWritingDirectionRightToLeft;
2198             break;
2199     }
2200
2201     return result;
2202 }
2203
2204 void FrameMac::tokenizerProcessedData()
2205 {
2206     if (d->m_doc)
2207         checkCompleted();
2208     loader()->checkLoadComplete();
2209 }
2210
2211 void FrameMac::setBridge(WebCoreFrameBridge *bridge)
2212
2213     if (_bridge == bridge)
2214         return;
2215
2216     HardRetain(bridge);
2217     HardRelease(_bridge);
2218     _bridge = bridge;
2219 }
2220
2221 String FrameMac::overrideMediaType() const
2222 {
2223     NSString *overrideType = [_bridge overrideMediaType];
2224     if (overrideType)
2225         return overrideType;
2226     return String();
2227 }
2228
2229 WebCoreKeyboardUIMode FrameMac::keyboardUIMode() const
2230 {
2231     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2232     return [_bridge keyboardUIMode];
2233     END_BLOCK_OBJC_EXCEPTIONS;
2234
2235     return WebCoreKeyboardAccessDefault;
2236 }
2237
2238 void FrameMac::didTellBridgeAboutLoad(const String& URL)
2239 {
2240     urlsBridgeKnowsAbout.add(URL);
2241 }
2242
2243 bool FrameMac::haveToldBridgeAboutLoad(const String& URL)
2244 {
2245     return urlsBridgeKnowsAbout.contains(URL);
2246 }
2247
2248 void FrameMac::clear(bool clearWindowProperties)
2249 {
2250     // FIXME: commenting the below line causes <http://bugs.webkit.org/show_bug.cgi?id=11212>, but putting it
2251     // back causes a measurable performance regression which we will need to fix to restore the correct behavior
2252     // urlsBridgeKnowsAbout.clear();
2253     setMarkedTextRange(0, nil, nil);
2254     Frame::clear();
2255 }
2256
2257 void FrameMac::print()
2258 {
2259     [Mac(this)->_bridge print];
2260 }
2261
2262 KJS::Bindings::Instance *FrameMac::getAppletInstanceForWidget(Widget *widget)
2263 {
2264     NSView *aView = widget->getView();
2265     if (!aView)
2266         return 0;
2267     jobject applet;
2268     
2269     // Get a pointer to the actual Java applet instance.
2270     if ([_bridge respondsToSelector:@selector(getAppletInView:)])
2271         applet = [_bridge getAppletInView:aView];
2272     else
2273         applet = [_bridge pollForAppletInView:aView];
2274     
2275     if (applet) {
2276         // Wrap the Java instance in a language neutral binding and hand
2277         // off ownership to the APPLET element.
2278         KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2279         KJS::Bindings::Instance *instance = KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::JavaLanguage, applet, executionContext);        
2280         return instance;
2281     }
2282     
2283     return 0;
2284 }
2285
2286 static KJS::Bindings::Instance *getInstanceForView(NSView *aView)
2287 {
2288     if ([aView respondsToSelector:@selector(objectForWebScript)]){
2289         id object = [aView objectForWebScript];
2290         if (object) {
2291             KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2292             return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::ObjectiveCLanguage, object, executionContext);
2293         }
2294     }
2295     else if ([aView respondsToSelector:@selector(createPluginScriptableObject)]) {
2296         NPObject *object = [aView createPluginScriptableObject];
2297         if (object) {
2298             KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction()(aView);
2299             KJS::Bindings::Instance *instance = KJS::Bindings::Instance::createBindingForLanguageInstance(KJS::Bindings::Instance::CLanguage, object, executionContext);
2300             
2301             // -createPluginScriptableObject returns a retained NPObject.  The caller is expected to release it.
2302             _NPN_ReleaseObject(object);
2303             
2304             return instance;
2305         }
2306     }
2307     return 0;
2308 }
2309
2310 KJS::Bindings::Instance *FrameMac::getEmbedInstanceForWidget(Widget *widget)
2311 {
2312     return getInstanceForView(widget->getView());
2313 }
2314
2315 KJS::Bindings::Instance *FrameMac::getObjectInstanceForWidget(Widget *widget)
2316 {
2317     return getInstanceForView(widget->getView());
2318 }
2319
2320 void FrameMac::addPluginRootObject(KJS::Bindings::RootObject *root)
2321 {
2322     m_rootObjects.append(root);
2323 }
2324
2325 void FrameMac::cleanupPluginObjects()
2326 {
2327     // Delete old plug-in data structures
2328     JSLock lock;
2329     
2330     unsigned count = m_rootObjects.size();
2331     for (unsigned i = 0; i < count; i++)
2332         m_rootObjects[i]->removeAllNativeReferences();
2333     m_rootObjects.clear();
2334     
2335     _bindingRoot = 0;
2336     HardRelease(_windowScriptObject);
2337     _windowScriptObject = 0;
2338     
2339     if (_windowScriptNPObject) {
2340         // Call _NPN_DeallocateObject() instead of _NPN_ReleaseObject() so that we don't leak if a plugin fails to release the window
2341         // script object properly.
2342         // This shouldn't cause any problems for plugins since they should have already been stopped and destroyed at this point.
2343         _NPN_DeallocateObject(_windowScriptNPObject);
2344         _windowScriptNPObject = 0;
2345     }
2346 }
2347
2348 void FrameMac::registerCommandForUndoOrRedo(PassRefPtr<EditCommand> cmd, bool isRedo)
2349 {
2350     ASSERT(cmd);
2351     WebUndoAction action = static_cast<WebUndoAction>(cmd->editingAction());
2352     NSUndoManager* undoManager = [_bridge undoManager];
2353     WebCoreEditCommand* command = [WebCoreEditCommand commandWithEditCommand:cmd];
2354     NSString* actionName = [_bridge nameForUndoAction:action];
2355     [undoManager registerUndoWithTarget:_bridge selector:(isRedo ? @selector(redoEditing:) : @selector(undoEditing:)) object:command];
2356     if (actionName)
2357         [undoManager setActionName:actionName];
2358     _haveUndoRedoOperations = YES;
2359 }
2360
2361 void FrameMac::registerCommandForUndo(PassRefPtr<EditCommand> cmd)
2362 {
2363     registerCommandForUndoOrRedo(cmd, false);
2364 }
2365
2366 void FrameMac::registerCommandForRedo(PassRefPtr<EditCommand> cmd)
2367 {
2368     registerCommandForUndoOrRedo(cmd, true);
2369 }
2370
2371 void FrameMac::clearUndoRedoOperations()
2372 {
2373     if (_haveUndoRedoOperations) {
2374         // workaround for <rdar://problem/4645507> NSUndoManager dies
2375         // with uncaught exception when undo items cleared while
2376         // groups are open
2377         NSUndoManager *undoManager = [_bridge undoManager];
2378         int groupingLevel = [undoManager groupingLevel];
2379         for (int i = 0; i < groupingLevel; ++i)
2380             [undoManager endUndoGrouping];
2381         
2382         [undoManager removeAllActionsWithTarget:_bridge];
2383
2384         for (int i = 0; i < groupingLevel; ++i)
2385             [undoManager beginUndoGrouping];
2386
2387         _haveUndoRedoOperations = NO;
2388     }
2389 }
2390
2391 void FrameMac::issueUndoCommand()
2392 {
2393     if (canUndo())
2394         [[_bridge undoManager] undo];
2395 }
2396
2397 void FrameMac::issueRedoCommand()
2398 {
2399     if (canRedo())
2400         [[_bridge undoManager] redo];
2401 }
2402
2403 void FrameMac::issueCutCommand()
2404 {
2405     [_bridge issueCutCommand];
2406 }
2407
2408 void FrameMac::issueCopyCommand()
2409 {
2410     [_bridge issueCopyCommand];
2411 }
2412
2413 void FrameMac::issuePasteCommand()
2414 {
2415     [_bridge issuePasteCommand];
2416 }
2417
2418 void FrameMac::issuePasteAndMatchStyleCommand()
2419 {
2420     [_bridge issuePasteAndMatchStyleCommand];
2421 }
2422
2423 void FrameMac::issueTransposeCommand()
2424 {
2425     [_bridge issueTransposeCommand];
2426 }
2427
2428 bool FrameMac::canUndo() const
2429 {
2430     return [[Mac(this)->_bridge undoManager] canUndo];
2431 }
2432
2433 bool FrameMac::canRedo() const
2434 {
2435     return [[Mac(this)->_bridge undoManager] canRedo];
2436 }
2437
2438 bool FrameMac::canPaste() const
2439 {
2440     return [Mac(this)->_bridge canPaste];
2441 }
2442
2443 void FrameMac::markMisspellingsInAdjacentWords(const VisiblePosition &p)
2444 {
2445     if (!editor()->client()->isContinuousSpellCheckingEnabled())
2446         return;
2447     markMisspellings(Selection(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)));
2448 }
2449
2450 void FrameMac::markMisspellings(const Selection& selection)
2451 {
2452     // This function is called with a selection already expanded to word boundaries.
2453     // Might be nice to assert that here.
2454
2455     if (!editor()->client()->isContinuousSpellCheckingEnabled())
2456         return;
2457
2458     RefPtr<Range> searchRange(selection.toRange());
2459     if (!searchRange || searchRange->isDetached())
2460         return;
2461     
2462     // If we're not in an editable node, bail.
2463     int exception = 0;
2464     Node *editableNode = searchRange->startContainer(exception);
2465     if (!editableNode->isContentEditable())
2466         return;
2467     
2468     // Get the spell checker if it is available
2469     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
2470     if (checker == nil)
2471         return;
2472     
2473     WordAwareIterator it(searchRange.get());
2474     
2475     while (!it.atEnd()) {      // we may be starting at the end of the doc, and already by atEnd
2476         const UChar* chars = it.characters();
2477         int len = it.length();
2478         if (len > 1 || !DeprecatedChar(chars[0]).isSpace()) {
2479             NSString *chunk = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(chars) length:len freeWhenDone:NO];
2480             int startIndex = 0;
2481             // Loop over the chunk to find each misspelling in it.
2482             while (startIndex < len) {
2483                 NSRange misspelling = [checker checkSpellingOfString:chunk startingAt:startIndex language:nil wrap:NO inSpellDocumentWithTag:editor()->client()->spellCheckerDocumentTag() wordCount:NULL];
2484                 if (misspelling.length == 0)
2485                     break;
2486                 else {
2487                     // Build up result range and string.  Note the misspelling may span many text nodes,
2488                     // but the CharIterator insulates us from this complexity
2489                     RefPtr<Range> misspellingRange(rangeOfContents(document()));
2490                     CharacterIterator chars(it.range().get());
2491                     chars.advance(misspelling.location);
2492                     misspellingRange->setStart(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
2493                     chars.advance(misspelling.length);
2494                     misspellingRange->setEnd(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
2495                     // Mark misspelling in document.
2496                     document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
2497                     startIndex = misspelling.location + misspelling.length;
2498                 }
2499             }
2500             [chunk release];
2501         }
2502     
2503         it.advance();
2504     }
2505 }
2506
2507 void FrameMac::respondToChangedSelection(const Selection &oldSelection, bool closeTyping)
2508 {
2509     if (document()) {
2510         if (editor()->client()->isContinuousSpellCheckingEnabled()) {
2511             Selection oldAdjacentWords;
2512             
2513             // If this is a change in selection resulting from a delete operation, oldSelection may no longer
2514             // be in the document.
2515             if (oldSelection.start().node() && oldSelection.start().node()->inDocument()) {
2516                 VisiblePosition oldStart(oldSelection.visibleStart());
2517                 oldAdjacentWords = Selection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));   
2518             }
2519
2520             VisiblePosition newStart(selectionController()->selection().visibleStart());
2521             Selection newAdjacentWords(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
2522
2523             // When typing we check spelling elsewhere, so don't redo it here.
2524             if (closeTyping && oldAdjacentWords != newAdjacentWords)
2525                 markMisspellings(oldAdjacentWords);
2526
2527             // This only erases a marker in the first word of the selection.
2528             // Perhaps peculiar, but it matches AppKit.
2529             document()->removeMarkers(newAdjacentWords.toRange().get(), DocumentMarker::Spelling);
2530         } else
2531             // When continuous spell checking is off, no markers appear after the selection changes.
2532             document()->removeMarkers(DocumentMarker::Spelling);
2533     }
2534
2535     [_bridge respondToChangedSelection];
2536 }
2537
2538 bool FrameMac::shouldChangeSelection(const Selection& oldSelection, const Selection& newSelection, EAffinity affinity, bool stillSelecting) const
2539 {
2540     return [_bridge shouldChangeSelectedDOMRange:[DOMRange _rangeWith:oldSelection.toRange().get()]
2541                                       toDOMRange:[DOMRange _rangeWith:newSelection.toRange().get()]
2542                                         affinity:affinity
2543                                   stillSelecting:stillSelecting];
2544 }
2545
2546 bool FrameMac::shouldDeleteSelection(const Selection& selection) const
2547 {
2548     return [_bridge shouldDeleteSelectedDOMRange:[DOMRange _rangeWith:selection.toRange().get()]];
2549 }
2550
2551 void FrameMac::respondToChangedContents(const Selection& selection)
2552 {
2553     if (AXObjectCache::accessibilityEnabled()) {
2554         Node* node = selection.start().node();
2555         if (node)
2556             renderer()->document()->axObjectCache()->postNotification(node->renderer(), "AXValueChanged");
2557     }
2558     [_bridge respondToChangedContents];
2559 }
2560
2561 bool FrameMac::isContentEditable() const
2562 {
2563     return Frame::isContentEditable() || [_bridge isEditable];
2564 }
2565
2566 bool FrameMac::shouldBeginEditing(const Range *range) const
2567 {
2568     ASSERT(range);
2569     return [_bridge shouldBeginEditing:[DOMRange _rangeWith:const_cast<Range*>(range)]];
2570 }
2571
2572 bool FrameMac::shouldEndEditing(const Range *range) const
2573 {
2574     ASSERT(range);
2575     return [_bridge shouldEndEditing:[DOMRange _rangeWith:const_cast<Range*>(range)]];
2576 }
2577
2578 void FrameMac::didBeginEditing() const
2579 {
2580     [_bridge didBeginEditing];
2581 }
2582
2583 void FrameMac::didEndEditing() const
2584 {
2585     [_bridge didEndEditing];
2586 }
2587
2588 void FrameMac::textFieldDidBeginEditing(Element* input)
2589 {
2590     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2591     [_bridge textFieldDidBeginEditing:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
2592     END_BLOCK_OBJC_EXCEPTIONS;
2593 }
2594
2595 void FrameMac::textFieldDidEndEditing(Element* input)
2596 {
2597     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2598     [_bridge textFieldDidEndEditing:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
2599     END_BLOCK_OBJC_EXCEPTIONS;
2600 }
2601
2602 void FrameMac::textDidChangeInTextField(Element* input)
2603 {
2604     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2605     [_bridge textDidChangeInTextField:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
2606     END_BLOCK_OBJC_EXCEPTIONS;
2607 }
2608
2609 void FrameMac::textDidChangeInTextArea(Element* textarea)
2610 {
2611     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2612     [_bridge textDidChangeInTextArea:(DOMHTMLTextAreaElement *)[DOMElement _elementWith:textarea]];
2613     END_BLOCK_OBJC_EXCEPTIONS;
2614 }
2615
2616 bool FrameMac::doTextFieldCommandFromEvent(Element* input, const PlatformKeyboardEvent* event)
2617 {
2618     // FIXME: We might eventually need to make sure key bindings for editing work even with
2619     // events created with the DOM API. Those don't have a PlatformKeyboardEvent.
2620     if (!event)
2621         return false;
2622
2623     BOOL result = false;
2624     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2625     SEL selector = selectorForKeyEvent(event);
2626     if (selector)
2627         result = [_bridge textField:(DOMHTMLInputElement *)[DOMElement _elementWith:input] doCommandBySelector:selector];
2628     END_BLOCK_OBJC_EXCEPTIONS;
2629     return result;
2630 }
2631
2632 void FrameMac::textWillBeDeletedInTextField(Element* input)
2633 {
2634     // We're using the deleteBackward selector for all deletion operations since the autofill code treats all deletions the same way.
2635     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2636     [_bridge textField:(DOMHTMLInputElement *)[DOMElement _elementWith:input] doCommandBySelector:@selector(deleteBackward:)];
2637     END_BLOCK_OBJC_EXCEPTIONS;
2638 }
2639
2640 bool FrameMac::inputManagerHasMarkedText() const
2641 {
2642     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2643     return [[NSInputManager currentInputManager] hasMarkedText];
2644     END_BLOCK_OBJC_EXCEPTIONS
2645     return false;
2646 }
2647
2648 const short enableRomanKeyboardsOnly = -23;
2649 void FrameMac::setSecureKeyboardEntry(bool enable)
2650 {
2651     if (enable) {
2652         EnableSecureEventInput();
2653 // FIXME: KeyScript is deprecated in Leopard, we need a new solution for this <rdar://problem/4727607>
2654 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
2655         KeyScript(enableRomanKeyboardsOnly);
2656 #endif
2657     } else {
2658         DisableSecureEventInput();
2659 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
2660         KeyScript(smKeyEnableKybds);
2661 #endif
2662     }
2663 }
2664
2665 bool FrameMac::isSecureKeyboardEntry()
2666 {
2667     return IsSecureEventInputEnabled();
2668 }
2669
2670 static void convertAttributesToUnderlines(Vector<MarkedTextUnderline>& result, const Range *markedTextRange, NSArray *attributes, NSArray *ranges)
2671 {
2672     int exception = 0;
2673     int baseOffset = markedTextRange->startOffset(exception);
2674
2675     unsigned length = [attributes count];
2676     ASSERT([ranges count] == length);
2677
2678     for (unsigned i = 0; i < length; i++) {
2679         NSNumber *style = [[attributes objectAtIndex:i] objectForKey:NSUnderlineStyleAttributeName];
2680         if (!style)
2681             continue;
2682         NSRange range = [[ranges objectAtIndex:i] rangeValue];
2683         NSColor *color = [[attributes objectAtIndex:i] objectForKey:NSUnderlineColorAttributeName];
2684         Color qColor = Color::black;
2685         if (color) {
2686             NSColor* deviceColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
2687             qColor = Color(makeRGBA((int)(255 * [deviceColor redComponent]),
2688                                     (int)(255 * [deviceColor blueComponent]),
2689                                     (int)(255 * [deviceColor greenComponent]),
2690                                     (int)(255 * [deviceColor alphaComponent])));
2691         }
2692
2693         result.append(MarkedTextUnderline(range.location + baseOffset, 
2694                                           range.location + baseOffset + range.length, 
2695                                           qColor,
2696                                           [style intValue] > 1));
2697     }
2698 }
2699
2700 void FrameMac::setMarkedTextRange(const Range *range, NSArray *attributes, NSArray *ranges)
2701 {
2702     int exception = 0;
2703
2704     ASSERT(!range || range->startContainer(exception) == range->endContainer(exception));
2705     ASSERT(!range || range->collapsed(exception) || range->startContainer(exception)->isTextNode());
2706
2707     d->m_markedTextUnderlines.clear();
2708     if (attributes == nil)
2709         d->m_markedTextUsesUnderlines = false;
2710     else {
2711         d->m_markedTextUsesUnderlines = true;
2712         convertAttributesToUnderlines(d->m_markedTextUnderlines, range, attributes, ranges);
2713     }
2714
2715     if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
2716         m_markedTextRange->startContainer(exception)->renderer()->repaint();
2717
2718     if (range && range->collapsed(exception))
2719         m_markedTextRange = 0;
2720     else
2721         m_markedTextRange = const_cast<Range*>(range);
2722
2723     if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
2724         m_markedTextRange->startContainer(exception)->renderer()->repaint();
2725 }
2726
2727 bool FrameMac::canGoBackOrForward(int distance) const
2728 {
2729     return [_bridge canGoBackOrForward:distance];
2730 }
2731
2732 void FrameMac::didFirstLayout()
2733 {
2734     loader()->didFirstLayout();
2735 }
2736
2737 NSMutableDictionary *FrameMac::dashboardRegionsDictionary()
2738 {
2739     Document *doc = document();
2740     if (!doc)
2741         return nil;
2742
2743     const Vector<DashboardRegionValue>& regions = doc->dashboardRegions();
2744     size_t n = regions.size();
2745
2746     // Convert the DeprecatedValueList<DashboardRegionValue> into a NSDictionary of WebDashboardRegions
2747     NSMutableDictionary *webRegions = [NSMutableDictionary dictionaryWithCapacity:n];
2748     for (size_t i = 0; i < n; i++) {
2749         const DashboardRegionValue& region = regions[i];
2750
2751         if (region.type == StyleDashboardRegion::None)
2752             continue;
2753         
2754         NSString *label = region.label;
2755         WebDashboardRegionType type = WebDashboardRegionTypeNone;
2756         if (region.type == StyleDashboardRegion::Circle)
2757             type = WebDashboardRegionTypeCircle;
2758         else if (region.type == StyleDashboardRegion::Rectangle)
2759             type = WebDashboardRegionTypeRectangle;
2760         NSMutableArray *regionValues = [webRegions objectForKey:label];
2761         if (!regionValues) {
2762             regionValues = [[NSMutableArray alloc] initWithCapacity:1];
2763             [webRegions setObject:regionValues forKey:label];
2764             [regionValues release];
2765         }
2766         
2767         WebDashboardRegion *webRegion = [[WebDashboardRegion alloc] initWithRect:region.bounds clip:region.clip type:type];
2768         [regionValues addObject:webRegion];
2769         [webRegion release];
2770     }
2771     
2772     return webRegions;
2773 }
2774
2775 void FrameMac::dashboardRegionsChanged()
2776 {
2777     NSMutableDictionary *webRegions = dashboardRegionsDictionary();
2778     [_bridge dashboardRegionsChanged:webRegions];
2779 }
2780
2781 void FrameMac::willPopupMenu(NSMenu * menu)
2782 {
2783     [_bridge willPopupMenu:menu];
2784 }
2785
2786 bool FrameMac::isCharacterSmartReplaceExempt(UChar c, bool isPreviousChar)
2787 {
2788     return [_bridge isCharacterSmartReplaceExempt:c isPreviousCharacter:isPreviousChar];
2789 }
2790
2791 void FrameMac::handledOnloadEvents()
2792 {
2793     loader()->client()->dispatchDidHandleOnloadEvents();
2794 }
2795
2796 bool FrameMac::shouldClose()
2797 {
2798     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2799
2800     if (![_bridge canRunBeforeUnloadConfirmPanel])
2801         return true;
2802
2803     RefPtr<Document> doc = document();
2804     if (!doc)
2805         return true;
2806     HTMLElement* body = doc->body();
2807     if (!body)
2808         return true;
2809
2810     RefPtr<BeforeUnloadEvent> event = new BeforeUnloadEvent;
2811     event->setTarget(doc.get());
2812     doc->handleWindowEvent(event.get(), false);
2813
2814     if (!event->defaultPrevented() && doc)
2815         doc->defaultEventHandler(event.get());
2816     if (event->result().isNull())
2817         return true;
2818
2819     String text = event->result();
2820     text.replace('\\', backslashAsCurrencySymbol());
2821
2822     return [_bridge runBeforeUnloadConfirmPanelWithMessage:text];
2823
2824     END_BLOCK_OBJC_EXCEPTIONS;
2825
2826     return true;
2827 }
2828
2829 void FrameMac::dragSourceMovedTo(const PlatformMouseEvent& event)
2830 {
2831     if (_dragSrc && _dragSrcMayBeDHTML)
2832         // for now we don't care if event handler cancels default behavior, since there is none
2833         dispatchDragSrcEvent(dragEvent, event);
2834 }
2835
2836 void FrameMac::dragSourceEndedAt(const PlatformMouseEvent& event, NSDragOperation operation)
2837 {
2838     if (_dragSrc && _dragSrcMayBeDHTML) {
2839         _dragClipboard->setDestinationOperation(operation);
2840         // for now we don't care if event handler cancels default behavior, since there is none
2841         dispatchDragSrcEvent(dragendEvent, event);
2842     }
2843     freeClipboard();
2844     _dragSrc = 0;
2845 }
2846
2847 // returns if we should continue "default processing", i.e., whether eventhandler canceled
2848 bool FrameMac::dispatchDragSrcEvent(const AtomicString &eventType, const PlatformMouseEvent& event) const
2849 {
2850     bool noDefaultProc = d->m_view->dispatchDragEvent(eventType, _dragSrc.get(), event, _dragClipboard.get());
2851     return !noDefaultProc;
2852 }
2853
2854 void Frame::setNeedsReapplyStyles()
2855 {
2856     [Mac(this)->_bridge setNeedsReapplyStyles];
2857 }
2858
2859 FloatRect FrameMac::customHighlightLineRect(const AtomicString& type, const FloatRect& lineRect)
2860 {
2861     return [_bridge customHighlightRect:type forLine:lineRect];
2862 }
2863
2864 void FrameMac::paintCustomHighlight(const AtomicString& type, const FloatRect& boxRect, const FloatRect& lineRect, bool text, bool line)
2865 {
2866     [_bridge paintCustomHighlight:type forBox:boxRect onLine:lineRect behindText:text entireLine:line];
2867 }
2868
2869 KURL FrameMac::originalRequestURL() const
2870 {
2871     return [_bridge originalRequestURL];
2872 }
2873
2874 bool FrameMac::isLoadTypeReload()
2875 {
2876     return loader()->loadType() == FrameLoadTypeReload;
2877 }
2878
2879 int FrameMac::getHistoryLength()
2880 {
2881     return [_bridge historyLength];
2882 }
2883
2884 void FrameMac::goBackOrForward(int distance)
2885 {
2886     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2887     [_bridge goBackOrForward:distance];
2888     END_BLOCK_OBJC_EXCEPTIONS;
2889 }
2890
2891 KURL FrameMac::historyURL(int distance)
2892 {
2893     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2894     return KURL([_bridge historyURL:distance]);
2895     END_BLOCK_OBJC_EXCEPTIONS;
2896     return KURL();
2897 }
2898
2899 } // namespace WebCore