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