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