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