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