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