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, Element* ownerElement)
152     : Frame(page, ownerElement)
153     , _bridge(nil)
154     , _mouseDownView(nil)
155     , _sendingEventToSubview(false)
156     , _mouseDownMayStartDrag(false)
157     , _mouseDownMayStartSelect(false)
158     , _activationEventNumber(0)
159     , _bindingRoot(0)
160     , _windowScriptObject(0)
161     , _windowScriptNPObject(0)
162 {
163     d->m_extension = new BrowserExtensionMac(this);
164 }
165
166 FrameMac::~FrameMac()
167 {
168     setView(0);
169     freeClipboard();
170     clearRecordedFormValues();    
171     
172     [_bridge clearFrame];
173     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, Element* ownerElement, const String& referrer)
543 {
544     BEGIN_BLOCK_OBJC_EXCEPTIONS;
545     
546     BOOL allowsScrolling = YES;
547     int marginWidth = -1;
548     int marginHeight = -1;
549     if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) {
550         HTMLFrameElement* o = static_cast<HTMLFrameElement*>(ownerElement);
551         allowsScrolling = o->scrollingMode() != ScrollBarAlwaysOff;
552         marginWidth = o->getMarginWidth();
553         marginHeight = o->getMarginHeight();
554     }
555
556     WebCoreFrameBridge *childBridge = [_bridge createChildFrameNamed:name
557                                                              withURL:url.getNSURL()
558                                                             referrer:referrer 
559                                                           ownerElement:ownerElement
560                                                      allowsScrolling:allowsScrolling
561                                                          marginWidth:marginWidth
562                                                         marginHeight:marginHeight];
563     return [childBridge impl];
564
565     END_BLOCK_OBJC_EXCEPTIONS;
566     return 0;
567 }
568
569 void FrameMac::setView(FrameView *view)
570 {
571     // Detach the document now, so any onUnload handlers get run - if
572     // we wait until the view is destroyed, then things won't be
573     // hooked up enough for some JavaScript calls to work.
574     if (d->m_doc && view == 0)
575         d->m_doc->detach();
576     
577     d->m_view = view;
578     
579     // Delete old PlugIn data structures
580     cleanupPluginRootObjects();
581     _bindingRoot = 0;
582     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     if (!jScriptEnabled())
1045         return 0;
1046
1047     return bindingRootObject();
1048 }
1049
1050 KJS::Bindings::RootObject *FrameMac::bindingRootObject()
1051 {
1052     assert(jScriptEnabled());
1053     if (!_bindingRoot) {
1054         JSLock lock;
1055         _bindingRoot = new KJS::Bindings::RootObject(0);    // The root gets deleted by JavaScriptCore.
1056         KJS::JSObject *win = KJS::Window::retrieveWindow(this);
1057         _bindingRoot->setRootObjectImp (win);
1058         _bindingRoot->setInterpreter(jScript()->interpreter());
1059         addPluginRootObject (_bindingRoot);
1060     }
1061     return _bindingRoot;
1062 }
1063
1064 WebScriptObject *FrameMac::windowScriptObject()
1065 {
1066     if (!jScriptEnabled())
1067         return 0;
1068
1069     if (!_windowScriptObject) {
1070         KJS::JSLock lock;
1071         KJS::JSObject *win = KJS::Window::retrieveWindow(this);
1072         _windowScriptObject = KWQRetainNSRelease([[WebScriptObject alloc] _initWithJSObject:win originExecutionContext:bindingRootObject() executionContext:bindingRootObject()]);
1073     }
1074
1075     return _windowScriptObject;
1076 }
1077
1078 NPObject *FrameMac::windowScriptNPObject()
1079 {
1080     if (!_windowScriptNPObject) {
1081         if (jScriptEnabled()) {
1082             // JavaScript is enabled, so there is a JavaScript window object.  Return an NPObject bound to the window
1083             // object.
1084             KJS::JSObject *win = KJS::Window::retrieveWindow(this);
1085             assert(win);
1086             _windowScriptNPObject = _NPN_CreateScriptObject(0, win, bindingRootObject(), bindingRootObject());
1087         } else {
1088             // JavaScript is not enabled, so we cannot bind the NPObject to the JavaScript window object.
1089             // Instead, we create an NPObject of a different class, one which is not bound to a JavaScript object.
1090             _windowScriptNPObject = _NPN_CreateNoScriptObject();
1091         }
1092     }
1093
1094     return _windowScriptNPObject;
1095 }
1096
1097 void FrameMac::partClearedInBegin()
1098 {
1099     if (jScriptEnabled())
1100         [_bridge windowObjectCleared];
1101 }
1102
1103 void FrameMac::openURLFromPageCache(KWQPageState *state)
1104 {
1105     // It's safe to assume none of the KWQPageState methods will raise
1106     // exceptions, since KWQPageState is implemented by WebCore and
1107     // does not throw
1108
1109     Document *doc = [state document];
1110     Node *mousePressNode = [state mousePressNode];
1111     KURL *kurl = [state URL];
1112     SavedProperties *windowProperties = [state windowProperties];
1113     SavedProperties *locationProperties = [state locationProperties];
1114     SavedBuiltins *interpreterBuiltins = [state interpreterBuiltins];
1115     PausedTimeouts *timeouts = [state pausedTimeouts];
1116     
1117     cancelRedirection();
1118
1119     // We still have to close the previous part page.
1120     closeURL();
1121             
1122     d->m_bComplete = false;
1123     
1124     // Don't re-emit the load event.
1125     d->m_bLoadEventEmitted = true;
1126     
1127     // delete old status bar msg's from kjs (if it _was_ activated on last URL)
1128     if (jScriptEnabled()) {
1129         d->m_kjsStatusBarText = String();
1130         d->m_kjsDefaultStatusBarText = String();
1131     }
1132
1133     ASSERT(kurl);
1134     
1135     d->m_url = *kurl;
1136     
1137     // 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
1138     // data arrives) (Simon)
1139     if (url().protocol().startsWith("http") && !url().host().isEmpty() && url().path().isEmpty())
1140         d->m_url.setPath("/");
1141     
1142     // copy to m_workingURL after fixing url() above
1143     d->m_workingURL = url();
1144         
1145     started();
1146     
1147     // -----------begin-----------
1148     clear();
1149
1150     doc->setInPageCache(NO);
1151
1152     d->m_bCleared = false;
1153     d->m_bComplete = false;
1154     d->m_bLoadEventEmitted = false;
1155     d->m_referrer = url().url();
1156     
1157     setView(doc->view());
1158     
1159     d->m_doc = doc;
1160     d->m_mousePressNode = mousePressNode;
1161     d->m_decoder = doc->decoder();
1162
1163     updatePolicyBaseURL();
1164
1165     { // scope the lock
1166         JSLock lock;
1167         restoreWindowProperties(windowProperties);
1168         restoreLocationProperties(locationProperties);
1169         restoreInterpreterBuiltins(*interpreterBuiltins);
1170     }
1171
1172     resumeTimeouts(timeouts);
1173     
1174     checkCompleted();
1175 }
1176
1177 WebCoreFrameBridge *FrameMac::bridgeForWidget(const Widget *widget)
1178 {
1179     ASSERT_ARG(widget, widget);
1180     
1181     FrameMac *frame = Mac(frameForWidget(widget));
1182     ASSERT(frame);
1183     return frame->bridge();
1184 }
1185
1186 NSView *FrameMac::documentViewForNode(Node *node)
1187 {
1188     WebCoreFrameBridge *bridge = Mac(frameForNode(node))->bridge();
1189     return [bridge documentView];
1190 }
1191
1192 void FrameMac::saveDocumentState()
1193 {
1194     // Do not save doc state if the page has a password field and a form that would be submitted
1195     // via https
1196     if (!(d->m_doc && d->m_doc->hasPasswordField() && d->m_doc->hasSecureForm())) {
1197         BEGIN_BLOCK_OBJC_EXCEPTIONS;
1198         [_bridge saveDocumentState];
1199         END_BLOCK_OBJC_EXCEPTIONS;
1200     }
1201 }
1202
1203 void FrameMac::restoreDocumentState()
1204 {
1205     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1206     [_bridge restoreDocumentState];
1207     END_BLOCK_OBJC_EXCEPTIONS;
1208 }
1209
1210 String FrameMac::incomingReferrer() const
1211 {
1212     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1213     return [_bridge incomingReferrer];
1214     END_BLOCK_OBJC_EXCEPTIONS;
1215
1216     return String();
1217 }
1218
1219 void FrameMac::runJavaScriptAlert(const String& message)
1220 {
1221     String text = message;
1222     text.replace('\\', backslashAsCurrencySymbol());
1223     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1224     [_bridge runJavaScriptAlertPanelWithMessage:text];
1225     END_BLOCK_OBJC_EXCEPTIONS;
1226 }
1227
1228 bool FrameMac::runJavaScriptConfirm(const String& message)
1229 {
1230     String text = message;
1231     text.replace('\\', backslashAsCurrencySymbol());
1232
1233     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1234     return [_bridge runJavaScriptConfirmPanelWithMessage:text];
1235     END_BLOCK_OBJC_EXCEPTIONS;
1236
1237     return false;
1238 }
1239
1240 bool FrameMac::runJavaScriptPrompt(const String& prompt, const String& defaultValue, String& result)
1241 {
1242     String promptText = prompt;
1243     promptText.replace('\\', backslashAsCurrencySymbol());
1244     String defaultValueText = defaultValue;
1245     defaultValueText.replace('\\', backslashAsCurrencySymbol());
1246
1247     bool ok;
1248     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1249     NSString *returnedText = nil;
1250
1251     ok = [_bridge runJavaScriptTextInputPanelWithPrompt:prompt
1252         defaultText:defaultValue returningText:&returnedText];
1253
1254     if (ok) {
1255         result = String(returnedText);
1256         result.replace(backslashAsCurrencySymbol(), '\\');
1257     }
1258
1259     return ok;
1260     END_BLOCK_OBJC_EXCEPTIONS;
1261     
1262     return false;
1263 }
1264
1265 bool FrameMac::shouldInterruptJavaScript()
1266 {
1267     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1268     return [_bridge shouldInterruptJavaScript];
1269     END_BLOCK_OBJC_EXCEPTIONS;
1270     
1271     return false;
1272 }
1273
1274 bool FrameMac::locationbarVisible()
1275 {
1276     return [_bridge areToolbarsVisible];
1277 }
1278
1279 bool FrameMac::menubarVisible()
1280 {
1281     // The menubar is always on in Mac OS X UI
1282     return true;
1283 }
1284
1285 bool FrameMac::personalbarVisible()
1286 {
1287     return [_bridge areToolbarsVisible];
1288 }
1289
1290 bool FrameMac::statusbarVisible()
1291 {
1292     return [_bridge isStatusbarVisible];
1293 }
1294
1295 bool FrameMac::toolbarVisible()
1296 {
1297     return [_bridge areToolbarsVisible];
1298 }
1299
1300 void FrameMac::addMessageToConsole(const String &message, unsigned lineNumber, const String &sourceURL)
1301 {
1302     NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
1303         (NSString *)message, @"message",
1304         [NSNumber numberWithInt: lineNumber], @"lineNumber",
1305         (NSString *)sourceURL, @"sourceURL",
1306         NULL];
1307     [_bridge addMessageToConsole:dictionary];
1308 }
1309
1310 void FrameMac::createEmptyDocument()
1311 {
1312     // Although it's not completely clear from the name of this function,
1313     // it does nothing if we already have a document, and just creates an
1314     // empty one if we have no document at all.
1315     if (!d->m_doc) {
1316         BEGIN_BLOCK_OBJC_EXCEPTIONS;
1317         [_bridge loadEmptyDocumentSynchronously];
1318         END_BLOCK_OBJC_EXCEPTIONS;
1319
1320         updateBaseURLForEmptyDocument();
1321     }
1322 }
1323
1324 bool FrameMac::keyEvent(NSEvent *event)
1325 {
1326     bool result;
1327     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1328
1329     ASSERT([event type] == NSKeyDown || [event type] == NSKeyUp);
1330
1331     // Check for cases where we are too early for events -- possible unmatched key up
1332     // from pressing return in the location bar.
1333     Document *doc = document();
1334     if (!doc) {
1335         return false;
1336     }
1337     Node *node = doc->focusNode();
1338     if (!node) {
1339         if (doc->isHTMLDocument())
1340             node = doc->body();
1341         else
1342             node = doc->documentElement();
1343         if (!node)
1344             return false;
1345     }
1346     
1347     if ([event type] == NSKeyDown) {
1348         prepareForUserAction();
1349     }
1350
1351     NSEvent *oldCurrentEvent = _currentEvent;
1352     _currentEvent = KWQRetain(event);
1353
1354     PlatformKeyboardEvent qEvent(event);
1355     result = !EventTargetNodeCast(node)->dispatchKeyEvent(qEvent);
1356
1357     // We want to send both a down and a press for the initial key event.
1358     // To get KHTML to do this, we send a second KeyPress with "is repeat" set to true,
1359     // which causes it to send a press to the DOM.
1360     // That's not a great hack; it would be good to do this in a better way.
1361     if ([event type] == NSKeyDown && ![event isARepeat]) {
1362         PlatformKeyboardEvent repeatEvent(event, true);
1363         if (!EventTargetNodeCast(node)->dispatchKeyEvent(repeatEvent))
1364             result = true;
1365     }
1366
1367     ASSERT(_currentEvent == event);
1368     KWQRelease(event);
1369     _currentEvent = oldCurrentEvent;
1370
1371     return result;
1372
1373     END_BLOCK_OBJC_EXCEPTIONS;
1374
1375     return false;
1376 }
1377
1378 void FrameMac::handleMousePressEvent(const MouseEventWithHitTestResults& event)
1379 {
1380     bool singleClick = [_currentEvent clickCount] <= 1;
1381
1382     // If we got the event back, that must mean it wasn't prevented,
1383     // so it's allowed to start a drag or selection.
1384     _mouseDownMayStartSelect = canMouseDownStartSelect(event.targetNode());
1385     
1386     // Careful that the drag starting logic stays in sync with eventMayStartDrag()
1387     _mouseDownMayStartDrag = singleClick;
1388
1389     d->m_mousePressNode = event.targetNode();
1390     
1391     if (!passWidgetMouseDownEventToWidget(event, false)) {
1392         // We don't do this at the start of mouse down handling (before calling into WebCore),
1393         // because we don't want to do it until we know we didn't hit a widget.
1394         NSView *view = d->m_view->getDocumentView();
1395
1396         if (singleClick) {
1397             BEGIN_BLOCK_OBJC_EXCEPTIONS;
1398             if ([_bridge firstResponder] != view) {
1399                 [_bridge makeFirstResponder:view];
1400             }
1401             END_BLOCK_OBJC_EXCEPTIONS;
1402         }
1403
1404         Frame::handleMousePressEvent(event);
1405     }
1406 }
1407
1408 bool FrameMac::passMouseDownEventToWidget(Widget* widget)
1409 {
1410     // FIXME: this method always returns true
1411
1412     if (!widget) {
1413         LOG_ERROR("hit a RenderWidget without a corresponding Widget, means a frame is half-constructed");
1414         return true;
1415     }
1416
1417     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1418     
1419     NSView *nodeView = widget->getView();
1420     ASSERT(nodeView);
1421     ASSERT([nodeView superview]);
1422     NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[_currentEvent locationInWindow] fromView:nil]];
1423     if (view == nil) {
1424         LOG_ERROR("KHTML says we hit a RenderWidget, but AppKit doesn't agree we hit the corresponding NSView");
1425         return true;
1426     }
1427     
1428     if ([_bridge firstResponder] == view) {
1429         // In the case where we just became first responder, we should send the mouseDown:
1430         // to the NSTextField, not the NSTextField's editor. This code makes sure that happens.
1431         // If we don't do this, we see a flash of selected text when clicking in a text field.
1432         if (![_bridge wasFirstResponderAtMouseDownTime:view] && [view isKindOfClass:[NSTextView class]]) {
1433             NSView *superview = view;
1434             while (superview != nodeView) {
1435                 superview = [superview superview];
1436                 ASSERT(superview);
1437                 if ([superview isKindOfClass:[NSControl class]]) {
1438                     NSControl *control = static_cast<NSControl*>(superview);
1439                     if ([control currentEditor] == view) {
1440                         view = superview;
1441                     }
1442                     break;
1443                 }
1444             }
1445         }
1446     } else {
1447         // Normally [NSWindow sendEvent:] handles setting the first responder.
1448         // But in our case, the event was sent to the view representing the entire web page.
1449         if ([_currentEvent clickCount] <= 1 && [view acceptsFirstResponder] && [view needsPanelToBecomeKey]) {
1450             [_bridge makeFirstResponder:view];
1451         }
1452     }
1453
1454     // We need to "defer loading" and defer timers while we are tracking the mouse.
1455     // That's because we don't want the new page to load while the user is holding the mouse down.
1456     
1457     BOOL wasDeferringLoading = [_bridge defersLoading];
1458     if (!wasDeferringLoading)
1459         [_bridge setDefersLoading:YES];
1460     BOOL wasDeferringTimers = isDeferringTimers();
1461     if (!wasDeferringTimers)
1462         setDeferringTimers(true);
1463
1464     ASSERT(!_sendingEventToSubview);
1465     _sendingEventToSubview = true;
1466     [view mouseDown:_currentEvent];
1467     _sendingEventToSubview = false;
1468     
1469     if (!wasDeferringTimers)
1470         setDeferringTimers(false);
1471     if (!wasDeferringLoading)
1472         [_bridge setDefersLoading:NO];
1473
1474     // Remember which view we sent the event to, so we can direct the release event properly.
1475     _mouseDownView = view;
1476     _mouseDownWasInSubframe = false;
1477
1478     END_BLOCK_OBJC_EXCEPTIONS;
1479
1480     return true;
1481 }
1482
1483 bool FrameMac::lastEventIsMouseUp() const
1484 {
1485     // Many AK widgets run their own event loops and consume events while the mouse is down.
1486     // When they finish, currentEvent is the mouseUp that they exited on.  We need to update
1487     // the khtml state with this mouseUp, which khtml never saw.  This method lets us detect
1488     // that state.
1489
1490     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1491     NSEvent *currentEventAfterHandlingMouseDown = [NSApp currentEvent];
1492     if (_currentEvent != currentEventAfterHandlingMouseDown) {
1493         if ([currentEventAfterHandlingMouseDown type] == NSLeftMouseUp) {
1494             return true;
1495         }
1496     }
1497     END_BLOCK_OBJC_EXCEPTIONS;
1498
1499     return false;
1500 }
1501     
1502 // Note that this does the same kind of check as [target isDescendantOf:superview].
1503 // There are two differences: This is a lot slower because it has to walk the whole
1504 // tree, and this works in cases where the target has already been deallocated.
1505 static bool findViewInSubviews(NSView *superview, NSView *target)
1506 {
1507     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1508     NSEnumerator *e = [[superview subviews] objectEnumerator];
1509     NSView *subview;
1510     while ((subview = [e nextObject])) {
1511         if (subview == target || findViewInSubviews(subview, target)) {
1512             return true;
1513         }
1514     }
1515     END_BLOCK_OBJC_EXCEPTIONS;
1516     
1517     return false;
1518 }
1519
1520 NSView *FrameMac::mouseDownViewIfStillGood()
1521 {
1522     // Since we have no way of tracking the lifetime of _mouseDownView, we have to assume that
1523     // it could be deallocated already. We search for it in our subview tree; if we don't find
1524     // it, we set it to nil.
1525     NSView *mouseDownView = _mouseDownView;
1526     if (!mouseDownView) {
1527         return nil;
1528     }
1529     FrameView *topFrameView = d->m_view.get();
1530     NSView *topView = topFrameView ? topFrameView->getView() : nil;
1531     if (!topView || !findViewInSubviews(topView, mouseDownView)) {
1532         _mouseDownView = nil;
1533         return nil;
1534     }
1535     return mouseDownView;
1536 }
1537
1538 bool FrameMac::eventMayStartDrag(NSEvent *event) const
1539 {
1540     // This is a pre-flight check of whether the event might lead to a drag being started.  Be careful
1541     // that its logic needs to stay in sync with handleMouseMoveEvent() and the way we set
1542     // _mouseDownMayStartDrag in handleMousePressEvent
1543     
1544     if ([event type] != NSLeftMouseDown || [event clickCount] != 1) {
1545         return false;
1546     }
1547     
1548     BOOL DHTMLFlag, UAFlag;
1549     [_bridge allowDHTMLDrag:&DHTMLFlag UADrag:&UAFlag];
1550     if (!DHTMLFlag && !UAFlag) {
1551         return false;
1552     }
1553
1554     NSPoint loc = [event locationInWindow];
1555     IntPoint mouseDownPos = d->m_view->viewportToContents(IntPoint(loc));
1556     RenderObject::NodeInfo nodeInfo(true, false);
1557     renderer()->layer()->hitTest(nodeInfo, mouseDownPos);
1558     bool srcIsDHTML;
1559     return nodeInfo.innerNode()->renderer()->draggableNode(DHTMLFlag, UAFlag, mouseDownPos.x(), mouseDownPos.y(), srcIsDHTML);
1560 }
1561
1562 // The link drag hysteresis is much larger than the others because there
1563 // needs to be enough space to cancel the link press without starting a link drag,
1564 // and because dragging links is rare.
1565 const float LinkDragHysteresis = 40.0;
1566 const float ImageDragHysteresis = 5.0;
1567 const float TextDragHysteresis = 3.0;
1568 const float GeneralDragHysteresis = 3.0;
1569 const float TextDragDelay = 0.15;
1570
1571 bool FrameMac::dragHysteresisExceeded(float dragLocationX, float dragLocationY) const
1572 {
1573     IntPoint dragViewportLocation((int)dragLocationX, (int)dragLocationY);
1574     IntPoint dragLocation = d->m_view->viewportToContents(dragViewportLocation);
1575     IntSize delta = dragLocation - m_mouseDownPos;
1576     
1577     float threshold = GeneralDragHysteresis;
1578     if (_dragSrcIsImage)
1579         threshold = ImageDragHysteresis;
1580     else if (_dragSrcIsLink)
1581         threshold = LinkDragHysteresis;
1582     else if (_dragSrcInSelection)
1583         threshold = TextDragHysteresis;
1584
1585     return fabsf(delta.width()) >= threshold || fabsf(delta.height()) >= threshold;
1586 }
1587
1588 void FrameMac::handleMouseMoveEvent(const MouseEventWithHitTestResults& event)
1589 {
1590     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1591
1592     if ([_currentEvent type] == NSLeftMouseDragged) {
1593         NSView *view = mouseDownViewIfStillGood();
1594
1595         if (view) {
1596             _sendingEventToSubview = true;
1597             [view mouseDragged:_currentEvent];
1598             _sendingEventToSubview = false;
1599             return;
1600         }
1601
1602         // Careful that the drag starting logic stays in sync with eventMayStartDrag()
1603     
1604         if (_mouseDownMayStartDrag && !_dragSrc) {
1605             BOOL tempFlag1, tempFlag2;
1606             [_bridge allowDHTMLDrag:&tempFlag1 UADrag:&tempFlag2];
1607             _dragSrcMayBeDHTML = tempFlag1;
1608             _dragSrcMayBeUA = tempFlag2;
1609             if (!_dragSrcMayBeDHTML && !_dragSrcMayBeUA) {
1610                 _mouseDownMayStartDrag = false;     // no element is draggable
1611             }
1612         }
1613         
1614         if (_mouseDownMayStartDrag && !_dragSrc) {
1615             // try to find an element that wants to be dragged
1616             RenderObject::NodeInfo nodeInfo(true, false);
1617             renderer()->layer()->hitTest(nodeInfo, m_mouseDownPos);
1618             Node *node = nodeInfo.innerNode();
1619             _dragSrc = (node && node->renderer()) ? node->renderer()->draggableNode(_dragSrcMayBeDHTML, _dragSrcMayBeUA, m_mouseDownPos.x(), m_mouseDownPos.y(), _dragSrcIsDHTML) : 0;
1620             if (!_dragSrc) {
1621                 _mouseDownMayStartDrag = false;     // no element is draggable
1622             } else {
1623                 // remember some facts about this source, while we have a NodeInfo handy
1624                 node = nodeInfo.URLElement();
1625                 _dragSrcIsLink = node && node->isLink();
1626
1627                 node = nodeInfo.innerNonSharedNode();
1628                 _dragSrcIsImage = node && node->renderer() && node->renderer()->isImage();
1629                 
1630                 _dragSrcInSelection = isPointInsideSelection(m_mouseDownPos);
1631             }                
1632         }
1633         
1634         // For drags starting in the selection, the user must wait between the mousedown and mousedrag,
1635         // or else we bail on the dragging stuff and allow selection to occur
1636         if (_mouseDownMayStartDrag && _dragSrcInSelection && [_currentEvent timestamp] - _mouseDownTimestamp < TextDragDelay) {
1637             _mouseDownMayStartDrag = false;
1638             // ...but if this was the first click in the window, we don't even want to start selection
1639             if (_activationEventNumber == [_currentEvent eventNumber]) {
1640                 _mouseDownMayStartSelect = false;
1641             }
1642         }
1643
1644         if (_mouseDownMayStartDrag) {
1645             // We are starting a text/image/url drag, so the cursor should be an arrow
1646             d->m_view->setCursor(pointerCursor());
1647             
1648             NSPoint dragLocation = [_currentEvent locationInWindow];
1649             if (dragHysteresisExceeded(dragLocation.x, dragLocation.y)) {
1650                 
1651                 // Once we're past the hysteresis point, we don't want to treat this gesture as a click
1652                 d->m_view->invalidateClick();
1653
1654                 NSImage *dragImage = nil;       // we use these values if WC is out of the loop
1655                 NSPoint dragLoc = NSZeroPoint;
1656                 NSDragOperation srcOp = NSDragOperationNone;                
1657                 BOOL wcWrotePasteboard = NO;
1658                 if (_dragSrcMayBeDHTML) {
1659                     NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
1660                     // Must be done before ondragstart adds types and data to the pboard,
1661                     // also done for security, as it erases data from the last drag
1662                     [pasteboard declareTypes:[NSArray array] owner:nil];
1663                     
1664                     freeClipboard();    // would only happen if we missed a dragEnd.  Do it anyway, just
1665                                         // to make sure it gets numbified
1666                     _dragClipboard = new ClipboardMac(true, pasteboard, ClipboardMac::Writable, this);
1667                     
1668                     // If this is drag of an element, get set up to generate a default image.  Otherwise
1669                     // WebKit will generate the default, the element doesn't override.
1670                     if (_dragSrcIsDHTML) {
1671                         int srcX, srcY;
1672                         _dragSrc->renderer()->absolutePosition(srcX, srcY);
1673                         IntSize delta = m_mouseDownPos - IntPoint(srcX, srcY);
1674                         _dragClipboard->setDragImageElement(_dragSrc.get(), IntPoint() + delta);
1675                     } 
1676
1677                     _mouseDownMayStartDrag = dispatchDragSrcEvent(dragstartEvent, m_mouseDown);
1678                     // Invalidate clipboard here against anymore pasteboard writing for security.  The drag
1679                     // image can still be changed as we drag, but not the pasteboard data.
1680                     _dragClipboard->setAccessPolicy(ClipboardMac::ImageWritable);
1681                     
1682                     if (_mouseDownMayStartDrag) {
1683                         // gather values from DHTML element, if it set any
1684                         _dragClipboard->sourceOperation(&srcOp);
1685
1686                         NSArray *types = [pasteboard types];
1687                         wcWrotePasteboard = types && [types count] > 0;
1688
1689                         if (_dragSrcMayBeDHTML) {
1690                             dragImage = _dragClipboard->dragNSImage(&dragLoc);
1691                         }
1692                         
1693                         // Yuck, dragSourceMovedTo() can be called as a result of kicking off the drag with
1694                         // dragImage!  Because of that dumb reentrancy, we may think we've not started the
1695                         // drag when that happens.  So we have to assume it's started before we kick it off.
1696                         _dragClipboard->setDragHasStarted();
1697                     }
1698                 }
1699                 
1700                 if (_mouseDownMayStartDrag) {
1701                     BOOL startedDrag = [_bridge startDraggingImage:dragImage at:dragLoc operation:srcOp event:_currentEvent sourceIsDHTML:_dragSrcIsDHTML DHTMLWroteData:wcWrotePasteboard];
1702                     if (!startedDrag && _dragSrcMayBeDHTML) {
1703                         // WebKit canned the drag at the last minute - we owe _dragSrc a DRAGEND event
1704                         PlatformMouseEvent event;
1705                         dispatchDragSrcEvent(dragendEvent, event);
1706                         _mouseDownMayStartDrag = false;
1707                     }
1708                 } 
1709
1710                 if (!_mouseDownMayStartDrag) {
1711                     // something failed to start the drag, cleanup
1712                     freeClipboard();
1713                     _dragSrc = 0;
1714                 }
1715             }
1716
1717             // No more default handling (like selection), whether we're past the hysteresis bounds or not
1718             return;
1719         }
1720         if (!_mouseDownMayStartSelect) {
1721             return;
1722         }
1723
1724         // Don't allow dragging or click handling after we've started selecting.
1725         _mouseDownMayStartDrag = false;
1726         d->m_view->invalidateClick();
1727
1728         Node* node = event.targetNode();
1729         RenderLayer* layer = 0;
1730         if (node && node->renderer())
1731             layer = node->renderer()->enclosingLayer();
1732             
1733         // If the selection is contained in a layer that can scroll, that layer should handle the autoscroll
1734         // Otherwise, let the bridge handle it so the view can scroll itself.
1735         while (layer && !layer->shouldAutoscroll())
1736             layer = layer->parent();
1737         if (layer)
1738             handleAutoscroll(layer);
1739         else {
1740             if (!d->m_autoscrollTimer.isActive())
1741                 startAutoscrollTimer();
1742             [_bridge handleAutoscrollForMouseDragged:_currentEvent];
1743         }
1744             
1745     } else {
1746         // If we allowed the other side of the bridge to handle a drag
1747         // last time, then m_bMousePressed might still be set. So we
1748         // clear it now to make sure the next move after a drag
1749         // doesn't look like a drag.
1750         d->m_bMousePressed = false;
1751     }
1752
1753     Frame::handleMouseMoveEvent(event);
1754
1755     END_BLOCK_OBJC_EXCEPTIONS;
1756 }
1757
1758 // Returns whether caller should continue with "the default processing", which is the same as 
1759 // the event handler NOT setting the return value to false
1760 bool FrameMac::dispatchCPPEvent(const AtomicString &eventType, ClipboardMac::AccessPolicy policy)
1761 {
1762     Node* target = d->m_selection.start().element();
1763     if (!target && document())
1764         target = document()->body();
1765     if (!target)
1766         return true;
1767
1768     RefPtr<ClipboardMac> clipboard = new ClipboardMac(false, [NSPasteboard generalPasteboard], (ClipboardMac::AccessPolicy)policy);
1769
1770     ExceptionCode ec = 0;
1771     RefPtr<Event> evt = new ClipboardEvent(eventType, true, true, clipboard.get());
1772     EventTargetNodeCast(target)->dispatchEvent(evt, ec, true);
1773     bool noDefaultProcessing = evt->defaultPrevented();
1774
1775     // invalidate clipboard here for security
1776     clipboard->setAccessPolicy(ClipboardMac::Numb);
1777
1778     return !noDefaultProcessing;
1779 }
1780
1781 // WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items.  They
1782 // also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items.
1783 // We need to use onbeforecopy as a real menu enabler because we allow elements that are not
1784 // normally selectable to implement copy/paste (like divs, or a document body).
1785
1786 bool FrameMac::mayCut()
1787 {
1788     return !dispatchCPPEvent(beforecutEvent, ClipboardMac::Numb);
1789 }
1790
1791 bool FrameMac::mayCopy()
1792 {
1793     return !dispatchCPPEvent(beforecopyEvent, ClipboardMac::Numb);
1794 }
1795
1796 bool FrameMac::mayPaste()
1797 {
1798     return !dispatchCPPEvent(beforepasteEvent, ClipboardMac::Numb);
1799 }
1800
1801 bool FrameMac::tryCut()
1802 {
1803     // Must be done before oncut adds types and data to the pboard,
1804     // also done for security, as it erases data from the last copy/paste.
1805     [[NSPasteboard generalPasteboard] declareTypes:[NSArray array] owner:nil];
1806
1807     return !dispatchCPPEvent(cutEvent, ClipboardMac::Writable);
1808 }
1809
1810 bool FrameMac::tryCopy()
1811 {
1812     // Must be done before oncopy adds types and data to the pboard,
1813     // also done for security, as it erases data from the last copy/paste.
1814     [[NSPasteboard generalPasteboard] declareTypes:[NSArray array] owner:nil];
1815
1816     return !dispatchCPPEvent(copyEvent, ClipboardMac::Writable);
1817 }
1818
1819 bool FrameMac::tryPaste()
1820 {
1821     return !dispatchCPPEvent(pasteEvent, ClipboardMac::Readable);
1822 }
1823
1824 void FrameMac::handleMouseReleaseEvent(const MouseEventWithHitTestResults& event)
1825 {
1826     NSView *view = mouseDownViewIfStillGood();
1827     if (!view) {
1828         // If this was the first click in the window, we don't even want to clear the selection.
1829         // This case occurs when the user clicks on a draggable element, since we have to process
1830         // the mouse down and drag events to see if we might start a drag.  For other first clicks
1831         // in a window, we just don't acceptFirstMouse, and the whole down-drag-up sequence gets
1832         // ignored upstream of this layer.
1833         if (_activationEventNumber != [_currentEvent eventNumber])
1834             Frame::handleMouseReleaseEvent(event);
1835         return;
1836     }
1837     stopAutoscrollTimer();
1838     
1839     _sendingEventToSubview = true;
1840     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1841     [view mouseUp:_currentEvent];
1842     END_BLOCK_OBJC_EXCEPTIONS;
1843     _sendingEventToSubview = false;
1844 }
1845
1846 bool FrameMac::passSubframeEventToSubframe(MouseEventWithHitTestResults& event, Frame* subframePart)
1847 {
1848     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1849
1850     switch ([_currentEvent type]) {
1851         case NSMouseMoved: {
1852             ASSERT(subframePart);
1853             [Mac(subframePart)->bridge() mouseMoved:_currentEvent];
1854             return true;
1855         }
1856         
1857         case NSLeftMouseDown: {
1858             Node *node = event.targetNode();
1859             if (!node) {
1860                 return false;
1861             }
1862             RenderObject *renderer = node->renderer();
1863             if (!renderer || !renderer->isWidget()) {
1864                 return false;
1865             }
1866             Widget *widget = static_cast<RenderWidget*>(renderer)->widget();
1867             if (!widget || !widget->isFrameView())
1868                 return false;
1869             if (!passWidgetMouseDownEventToWidget(static_cast<RenderWidget*>(renderer))) {
1870                 return false;
1871             }
1872             _mouseDownWasInSubframe = true;
1873             return true;
1874         }
1875         case NSLeftMouseUp: {
1876             if (!_mouseDownWasInSubframe) {
1877                 return false;
1878             }
1879             NSView *view = mouseDownViewIfStillGood();
1880             if (!view) {
1881                 return false;
1882             }
1883             ASSERT(!_sendingEventToSubview);
1884             _sendingEventToSubview = true;
1885             [view mouseUp:_currentEvent];
1886             _sendingEventToSubview = false;
1887             return true;
1888         }
1889         case NSLeftMouseDragged: {
1890             if (!_mouseDownWasInSubframe) {
1891                 return false;
1892             }
1893             NSView *view = mouseDownViewIfStillGood();
1894             if (!view) {
1895                 return false;
1896             }
1897             ASSERT(!_sendingEventToSubview);
1898             _sendingEventToSubview = true;
1899             [view mouseDragged:_currentEvent];
1900             _sendingEventToSubview = false;
1901             return true;
1902         }
1903         default:
1904             return false;
1905     }
1906     END_BLOCK_OBJC_EXCEPTIONS;
1907
1908     return false;
1909 }
1910
1911 bool FrameMac::passWheelEventToChildWidget(Node *node)
1912 {
1913     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1914         
1915     if ([_currentEvent type] != NSScrollWheel || _sendingEventToSubview || !node) 
1916         return false;
1917     else {
1918         RenderObject *renderer = node->renderer();
1919         if (!renderer || !renderer->isWidget())
1920             return false;
1921         Widget *widget = static_cast<RenderWidget*>(renderer)->widget();
1922         if (!widget)
1923             return false;
1924             
1925         NSView *nodeView = widget->getView();
1926         ASSERT(nodeView);
1927         ASSERT([nodeView superview]);
1928         NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[_currentEvent locationInWindow] fromView:nil]];
1929     
1930         ASSERT(view);
1931         _sendingEventToSubview = true;
1932         [view scrollWheel:_currentEvent];
1933         _sendingEventToSubview = false;
1934         return true;
1935     }
1936             
1937     END_BLOCK_OBJC_EXCEPTIONS;
1938     return false;
1939 }
1940
1941 void FrameMac::mouseDown(NSEvent *event)
1942 {
1943     FrameView *v = d->m_view.get();
1944     if (!v || _sendingEventToSubview)
1945         return;
1946
1947     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1948
1949     prepareForUserAction();
1950
1951     _mouseDownView = nil;
1952     _dragSrc = 0;
1953     
1954     NSEvent *oldCurrentEvent = _currentEvent;
1955     _currentEvent = KWQRetain(event);
1956     m_mouseDown = PlatformMouseEvent(event);
1957     NSPoint loc = [event locationInWindow];
1958     m_mouseDownPos = d->m_view->viewportToContents(IntPoint(loc));
1959     _mouseDownTimestamp = [event timestamp];
1960
1961     _mouseDownMayStartDrag = false;
1962     _mouseDownMayStartSelect = false;
1963
1964     v->handleMousePressEvent(event);
1965     
1966     ASSERT(_currentEvent == event);
1967     KWQRelease(event);
1968     _currentEvent = oldCurrentEvent;
1969
1970     END_BLOCK_OBJC_EXCEPTIONS;
1971 }
1972
1973 void FrameMac::mouseDragged(NSEvent *event)
1974 {
1975     FrameView *v = d->m_view.get();
1976     if (!v || _sendingEventToSubview) {
1977         return;
1978     }
1979
1980     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1981
1982     NSEvent *oldCurrentEvent = _currentEvent;
1983     _currentEvent = KWQRetain(event);
1984
1985     v->handleMouseMoveEvent(event);
1986     
1987     ASSERT(_currentEvent == event);
1988     KWQRelease(event);
1989     _currentEvent = oldCurrentEvent;
1990
1991     END_BLOCK_OBJC_EXCEPTIONS;
1992 }
1993
1994 void FrameMac::mouseUp(NSEvent *event)
1995 {
1996     FrameView *v = d->m_view.get();
1997     if (!v || _sendingEventToSubview)
1998         return;
1999
2000     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2001
2002     NSEvent *oldCurrentEvent = _currentEvent;
2003     _currentEvent = KWQRetain(event);
2004
2005     // Our behavior here is a little different that Qt. Qt always sends
2006     // a mouse release event, even for a double click. To correct problems
2007     // in khtml's DOM click event handling we do not send a release here
2008     // for a double click. Instead we send that event from FrameView's
2009     // handleMouseDoubleClickEvent. Note also that the third click of
2010     // a triple click is treated as a single click, but the fourth is then
2011     // treated as another double click. Hence the "% 2" below.
2012     int clickCount = [event clickCount];
2013     if (clickCount > 0 && clickCount % 2 == 0)
2014         v->handleMouseDoubleClickEvent(event);
2015     else
2016         v->handleMouseReleaseEvent(event);
2017     
2018     ASSERT(_currentEvent == event);
2019     KWQRelease(event);
2020     _currentEvent = oldCurrentEvent;
2021     
2022     _mouseDownView = nil;
2023
2024     END_BLOCK_OBJC_EXCEPTIONS;
2025 }
2026
2027 /*
2028  A hack for the benefit of AK's PopUpButton, which uses the Carbon menu manager, which thus
2029  eats all subsequent events after it is starts its modal tracking loop.  After the interaction
2030  is done, this routine is used to fix things up.  When a mouse down started us tracking in
2031  the widget, we post a fake mouse up to balance the mouse down we started with. When a 
2032  key down started us tracking in the widget, we post a fake key up to balance things out.
2033  In addition, we post a fake mouseMoved to get the cursor in sync with whatever we happen to 
2034  be over after the tracking is done.
2035  */
2036 void FrameMac::sendFakeEventsAfterWidgetTracking(NSEvent *initiatingEvent)
2037 {
2038     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2039
2040     _sendingEventToSubview = false;
2041     int eventType = [initiatingEvent type];
2042     if (eventType == NSLeftMouseDown || eventType == NSKeyDown) {
2043         NSEvent *fakeEvent = nil;
2044         if (eventType == NSLeftMouseDown) {
2045             fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
2046                                     location:[initiatingEvent locationInWindow]
2047                                 modifierFlags:[initiatingEvent modifierFlags]
2048                                     timestamp:[initiatingEvent timestamp]
2049                                 windowNumber:[initiatingEvent windowNumber]
2050                                         context:[initiatingEvent context]
2051                                     eventNumber:[initiatingEvent eventNumber]
2052                                     clickCount:[initiatingEvent clickCount]
2053                                     pressure:[initiatingEvent pressure]];
2054         
2055             mouseUp(fakeEvent);
2056         }
2057         else { // eventType == NSKeyDown
2058             fakeEvent = [NSEvent keyEventWithType:NSKeyUp
2059                                     location:[initiatingEvent locationInWindow]
2060                                modifierFlags:[initiatingEvent modifierFlags]
2061                                    timestamp:[initiatingEvent timestamp]
2062                                 windowNumber:[initiatingEvent windowNumber]
2063                                      context:[initiatingEvent context]
2064                                   characters:[initiatingEvent characters] 
2065                  charactersIgnoringModifiers:[initiatingEvent charactersIgnoringModifiers] 
2066                                    isARepeat:[initiatingEvent isARepeat] 
2067                                      keyCode:[initiatingEvent keyCode]];
2068             keyEvent(fakeEvent);
2069         }
2070         // FIXME:  We should really get the current modifierFlags here, but there's no way to poll
2071         // them in Cocoa, and because the event stream was stolen by the Carbon menu code we have
2072         // no up-to-date cache of them anywhere.
2073         fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
2074                                        location:[[_bridge window] convertScreenToBase:[NSEvent mouseLocation]]
2075                                   modifierFlags:[initiatingEvent modifierFlags]
2076                                       timestamp:[initiatingEvent timestamp]
2077                                    windowNumber:[initiatingEvent windowNumber]
2078                                         context:[initiatingEvent context]
2079                                     eventNumber:0
2080                                      clickCount:0
2081                                        pressure:0];
2082         mouseMoved(fakeEvent);
2083     }
2084     
2085     END_BLOCK_OBJC_EXCEPTIONS;
2086 }
2087
2088 void FrameMac::mouseMoved(NSEvent *event)
2089 {
2090     FrameView *v = d->m_view.get();
2091     // Reject a mouse moved if the button is down - screws up tracking during autoscroll
2092     // These happen because WebKit sometimes has to fake up moved events.
2093     if (!v || d->m_bMousePressed || _sendingEventToSubview)
2094         return;
2095     
2096     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2097
2098     NSEvent *oldCurrentEvent = _currentEvent;
2099     _currentEvent = KWQRetain(event);
2100     
2101     v->handleMouseMoveEvent(event);
2102     
2103     ASSERT(_currentEvent == event);
2104     KWQRelease(event);
2105     _currentEvent = oldCurrentEvent;
2106
2107     END_BLOCK_OBJC_EXCEPTIONS;
2108 }
2109
2110 // Called as we walk up the element chain for nodes with CSS property -webkit-user-drag == auto
2111 bool FrameMac::shouldDragAutoNode(Node* node, const IntPoint& point) const
2112 {
2113     // We assume that WebKit only cares about dragging things that can be leaf nodes (text, images, urls).
2114     // This saves a bunch of expensive calls (creating WC and WK element dicts) as we walk farther up
2115     // the node hierarchy, and we also don't have to cook up a way to ask WK about non-leaf nodes
2116     // (since right now WK just hit-tests using a cached lastMouseDown).
2117     if (!node->hasChildNodes() && d->m_view) {
2118         NSPoint eventLoc = d->m_view->contentsToViewport(point);
2119         return [_bridge mayStartDragAtEventLocation:eventLoc];
2120     } else
2121         return NO;
2122 }
2123
2124 bool FrameMac::sendContextMenuEvent(NSEvent *event)
2125 {
2126     Document* doc = d->m_doc.get();
2127     FrameView* v = d->m_view.get();
2128     if (!doc || !v)
2129         return false;
2130
2131     bool swallowEvent;
2132     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2133
2134     NSEvent *oldCurrentEvent = _currentEvent;
2135     _currentEvent = KWQRetain(event);
2136     
2137     PlatformMouseEvent mouseEvent(event);
2138
2139     IntPoint viewportPos = v->viewportToContents(mouseEvent.pos());
2140     MouseEventWithHitTestResults mev = doc->prepareMouseEvent(false, true, false, viewportPos, mouseEvent);
2141
2142     swallowEvent = v->dispatchMouseEvent(contextmenuEvent, mev.targetNode(), true, 0, mouseEvent, true);
2143     if (!swallowEvent && !isPointInsideSelection(viewportPos) &&
2144             ([_bridge selectWordBeforeMenuEvent] || [_bridge isEditable]
2145                 || (mev.targetNode() && mev.targetNode()->isContentEditable()))) {
2146         _mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection
2147         selectClosestWordFromMouseEvent(mouseEvent, mev.targetNode());
2148     }
2149
2150     ASSERT(_currentEvent == event);
2151     KWQRelease(event);
2152     _currentEvent = oldCurrentEvent;
2153
2154     return swallowEvent;
2155
2156     END_BLOCK_OBJC_EXCEPTIONS;
2157
2158     return false;
2159 }
2160
2161 struct ListItemInfo {
2162     unsigned start;
2163     unsigned end;
2164 };
2165
2166 NSFileWrapper *FrameMac::fileWrapperForElement(Element *e)
2167 {
2168     NSFileWrapper *wrapper = nil;
2169     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2170     
2171     const AtomicString& attr = e->getAttribute(srcAttr);
2172     if (!attr.isEmpty()) {
2173         NSURL *URL = completeURL(attr.deprecatedString()).getNSURL();
2174         wrapper = [_bridge fileWrapperForURL:URL];
2175     }    
2176     if (!wrapper) {
2177         RenderImage *renderer = static_cast<RenderImage*>(e->renderer());
2178         if (renderer->cachedImage() && !renderer->cachedImage()->isErrorImage()) {
2179             wrapper = [[NSFileWrapper alloc] initRegularFileWithContents:(NSData*)(renderer->cachedImage()->image()->getTIFFRepresentation())];
2180             [wrapper setPreferredFilename:@"image.tiff"];
2181             [wrapper autorelease];
2182         }
2183     }
2184
2185     return wrapper;
2186
2187     END_BLOCK_OBJC_EXCEPTIONS;
2188
2189     return nil;
2190 }
2191
2192 static Element *listParent(Element *item)
2193 {
2194     while (!item->hasTagName(ulTag) && !item->hasTagName(olTag)) {
2195         item = static_cast<Element*>(item->parentNode());
2196         if (!item)
2197             break;
2198     }
2199     return item;
2200 }
2201
2202 static Node* isTextFirstInListItem(Node *e)
2203 {
2204     if (!e->isTextNode())
2205         return 0;
2206     Node* par = e->parentNode();
2207     while (par) {
2208         if (par->firstChild() != e)
2209             return 0;
2210         if (par->hasTagName(liTag))
2211             return par;
2212         e = par;
2213         par = par->parentNode();
2214     }
2215     return 0;
2216 }
2217
2218 // FIXME: This collosal function needs to be refactored into maintainable smaller bits.
2219
2220 #define BULLET_CHAR 0x2022
2221 #define SQUARE_CHAR 0x25AA
2222 #define CIRCLE_CHAR 0x25E6
2223
2224 NSAttributedString *FrameMac::attributedString(Node *_start, int startOffset, Node *endNode, int endOffset)
2225 {
2226     ListItemInfo info;
2227     NSMutableAttributedString *result;
2228     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2229
2230     Node * _startNode = _start;
2231
2232     if (!_startNode)
2233         return nil;
2234
2235     result = [[[NSMutableAttributedString alloc] init] autorelease];
2236
2237     bool hasNewLine = true;
2238     bool addedSpace = true;
2239     NSAttributedString *pendingStyledSpace = nil;
2240     bool hasParagraphBreak = true;
2241     const Element *linkStartNode = 0;
2242     unsigned linkStartLocation = 0;
2243     DeprecatedPtrList<Element> listItems;
2244     DeprecatedValueList<ListItemInfo> listItemLocations;
2245     float maxMarkerWidth = 0;
2246     
2247     Node *n = _startNode;
2248     
2249     // If the first item is the entire text of a list item, use the list item node as the start of the 
2250     // selection, not the text node.  The user's intent was probably to select the list.
2251     if (n->isTextNode() && startOffset == 0) {
2252         Node *startListNode = isTextFirstInListItem(_startNode);
2253         if (startListNode){
2254             _startNode = startListNode;
2255             n = _startNode;
2256         }
2257     }
2258     
2259     while (n) {
2260         RenderObject *renderer = n->renderer();
2261         if (renderer) {
2262             RenderStyle *style = renderer->style();
2263             NSFont *font = style->font().getNSFont();
2264             bool needSpace = pendingStyledSpace != nil;
2265             if (n->isTextNode()) {
2266                 if (hasNewLine) {
2267                     addedSpace = true;
2268                     needSpace = false;
2269                     [pendingStyledSpace release];
2270                     pendingStyledSpace = nil;
2271                     hasNewLine = false;
2272                 }
2273                 DeprecatedString text;
2274                 DeprecatedString str = n->nodeValue().deprecatedString();
2275                 int start = (n == _startNode) ? startOffset : -1;
2276                 int end = (n == endNode) ? endOffset : -1;
2277                 if (renderer->isText()) {
2278                     if (!style->collapseWhiteSpace()) {
2279                         if (needSpace && !addedSpace) {
2280                             if (text.isEmpty() && linkStartLocation == [result length])
2281                                 ++linkStartLocation;
2282                             [result appendAttributedString:pendingStyledSpace];
2283                         }
2284                         int runStart = (start == -1) ? 0 : start;
2285                         int runEnd = (end == -1) ? str.length() : end;
2286                         text += str.mid(runStart, runEnd-runStart);
2287                         [pendingStyledSpace release];
2288                         pendingStyledSpace = nil;
2289                         addedSpace = u_charDirection(str[runEnd - 1].unicode()) == U_WHITE_SPACE_NEUTRAL;
2290                     }
2291                     else {
2292                         RenderText* textObj = static_cast<RenderText*>(renderer);
2293                         if (!textObj->firstTextBox() && str.length() > 0 && !addedSpace) {
2294                             // We have no runs, but we do have a length.  This means we must be
2295                             // whitespace that collapsed away at the end of a line.
2296                             text += ' ';
2297                             addedSpace = true;
2298                         }
2299                         else {
2300                             addedSpace = false;
2301                             for (InlineTextBox* box = textObj->firstTextBox(); box; box = box->nextTextBox()) {
2302                                 int runStart = (start == -1) ? box->m_start : start;
2303                                 int runEnd = (end == -1) ? box->m_start + box->m_len : end;
2304                                 runEnd = min(runEnd, box->m_start + box->m_len);
2305                                 if (runStart >= box->m_start &&
2306                                     runStart < box->m_start + box->m_len) {
2307                                     if (box == textObj->firstTextBox() && box->m_start == runStart && runStart > 0)
2308                                         needSpace = true; // collapsed space at the start
2309                                     if (needSpace && !addedSpace) {
2310                                         if (pendingStyledSpace != nil) {
2311                                             if (text.isEmpty() && linkStartLocation == [result length])
2312                                                 ++linkStartLocation;
2313                                             [result appendAttributedString:pendingStyledSpace];
2314                                         } else
2315                                             text += ' ';
2316                                     }
2317                                     DeprecatedString runText = str.mid(runStart, runEnd - runStart);
2318                                     runText.replace('\n', ' ');
2319                                     text += runText;
2320                                     int nextRunStart = box->nextTextBox() ? box->nextTextBox()->m_start : str.length(); // collapsed space between runs or at the end
2321                                     needSpace = nextRunStart > runEnd;
2322                                     [pendingStyledSpace release];
2323                                     pendingStyledSpace = nil;
2324                                     addedSpace = u_charDirection(str[runEnd - 1].unicode()) == U_WHITE_SPACE_NEUTRAL;
2325                                     start = -1;
2326                                 }
2327                                 if (end != -1 && runEnd >= end)
2328                                     break;
2329                             }
2330                         }
2331                     }
2332                 }
2333                 
2334                 text.replace('\\', renderer->backslashAsCurrencySymbol());
2335     
2336                 if (text.length() > 0 || needSpace) {
2337                     NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init];
2338                     [attrs setObject:font forKey:NSFontAttributeName];
2339                     if (style && style->color().isValid() && style->color().alpha() != 0)
2340                         [attrs setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2341                     if (style && style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0)
2342                         [attrs setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2343
2344                     if (text.length() > 0) {
2345                         hasParagraphBreak = false;
2346                         NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString() attributes:attrs];
2347                         [result appendAttributedString: partialString];                
2348                         [partialString release];
2349                     }
2350
2351                     if (needSpace) {
2352                         [pendingStyledSpace release];
2353                         pendingStyledSpace = [[NSAttributedString alloc] initWithString:@" " attributes:attrs];
2354                     }
2355
2356                     [attrs release];
2357                 }
2358             } else {
2359                 // This is our simple HTML -> ASCII transformation:
2360                 DeprecatedString text;
2361                 if (n->hasTagName(aTag)) {
2362                     // Note the start of the <a> element.  We will add the NSLinkAttributeName
2363                     // attribute to the attributed string when navigating to the next sibling 
2364                     // of this node.
2365                     linkStartLocation = [result length];
2366                     linkStartNode = static_cast<Element*>(n);
2367                 } else if (n->hasTagName(brTag)) {
2368                     text += "\n";
2369                     hasNewLine = true;
2370                 } else if (n->hasTagName(liTag)) {
2371                     DeprecatedString listText;
2372                     Element *itemParent = listParent(static_cast<Element*>(n));
2373                     
2374                     if (!hasNewLine)
2375                         listText += '\n';
2376                     hasNewLine = true;
2377
2378                     listItems.append(static_cast<Element*>(n));
2379                     info.start = [result length];
2380                     info.end = 0;
2381                     listItemLocations.append (info);
2382                     
2383                     listText += '\t';
2384                     if (itemParent && renderer->isListItem()) {
2385                         RenderListItem* listRenderer = static_cast<RenderListItem*>(renderer);
2386
2387                         maxMarkerWidth = MAX([font pointSize], maxMarkerWidth);
2388                         switch(style->listStyleType()) {
2389                             case DISC:
2390                                 listText += ((QChar)BULLET_CHAR);
2391                                 break;
2392                             case CIRCLE:
2393                                 listText += ((QChar)CIRCLE_CHAR);
2394                                 break;
2395                             case SQUARE:
2396                                 listText += ((QChar)SQUARE_CHAR);
2397                                 break;
2398                             case LNONE:
2399                                 break;
2400                             default:
2401                                 DeprecatedString marker = listRenderer->markerStringValue();
2402                                 listText += marker;
2403                                 // Use AppKit metrics.  Will be rendered by AppKit.
2404                                 float markerWidth = [marker.getNSString() sizeWithAttributes:[NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]].width;
2405                                 maxMarkerWidth = MAX(markerWidth, maxMarkerWidth);
2406                         }
2407
2408                         listText += ' ';
2409                         listText += '\t';
2410
2411                         NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init];
2412                         [attrs setObject:font forKey:NSFontAttributeName];
2413                         if (style && style->color().isValid())
2414                             [attrs setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2415                         if (style && style->backgroundColor().isValid())
2416                             [attrs setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2417
2418                         NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:listText.getNSString() attributes:attrs];
2419                         [attrs release];
2420                         [result appendAttributedString: partialString];                
2421                         [partialString release];
2422                     }
2423                 } else if (n->hasTagName(olTag) || n->hasTagName(ulTag)) {
2424                     if (!hasNewLine)
2425                         text += "\n";
2426                     hasNewLine = true;
2427                 } else if (n->hasTagName(blockquoteTag)
2428                         || n->hasTagName(ddTag)
2429                         || n->hasTagName(divTag)
2430                         || n->hasTagName(dlTag)
2431                         || n->hasTagName(dtTag)
2432                         || n->hasTagName(hrTag)
2433                         || n->hasTagName(listingTag)
2434                         || n->hasTagName(preTag)
2435                         || n->hasTagName(tdTag)
2436                         || n->hasTagName(thTag)) {
2437                     if (!hasNewLine)
2438                         text += '\n';
2439                     hasNewLine = true;
2440                 } else if (n->hasTagName(h1Tag)
2441                         || n->hasTagName(h2Tag)
2442                         || n->hasTagName(h3Tag)
2443                         || n->hasTagName(h4Tag)
2444                         || n->hasTagName(h5Tag)
2445                         || n->hasTagName(h6Tag)
2446                         || n->hasTagName(pTag)
2447                         || n->hasTagName(trTag)) {
2448                     if (!hasNewLine)
2449                         text += '\n';
2450                     
2451                     // In certain cases, emit a paragraph break.
2452                     int bottomMargin = renderer->collapsedMarginBottom();
2453                     int fontSize = style->fontDescription().computedPixelSize();
2454                     if (bottomMargin * 2 >= fontSize) {
2455                         if (!hasParagraphBreak) {
2456                             text += '\n';
2457                             hasParagraphBreak = true;
2458                         }
2459                     }
2460                     
2461                     hasNewLine = true;
2462                 }
2463                 else if (n->hasTagName(imgTag)) {
2464                     if (pendingStyledSpace != nil) {
2465                         if (linkStartLocation == [result length])
2466                             ++linkStartLocation;
2467                         [result appendAttributedString:pendingStyledSpace];
2468                         [pendingStyledSpace release];
2469                         pendingStyledSpace = nil;
2470                     }
2471                     NSFileWrapper *fileWrapper = fileWrapperForElement(static_cast<Element*>(n));
2472                     NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWrapper:fileWrapper];
2473                     NSAttributedString *iString = [NSAttributedString attributedStringWithAttachment:attachment];
2474                     [result appendAttributedString: iString];
2475                     [attachment release];
2476                 }
2477
2478                 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()];
2479                 [result appendAttributedString: partialString];
2480                 [partialString release];
2481             }
2482         }
2483
2484         if (n == endNode)
2485             break;
2486
2487         Node *next = n->firstChild();
2488         if (!next)
2489             next = n->nextSibling();
2490
2491         while (!next && n->parentNode()) {
2492             DeprecatedString text;
2493             n = n->parentNode();
2494             if (n == endNode)
2495                 break;
2496             next = n->nextSibling();
2497
2498             if (n->hasTagName(aTag)) {
2499                 // End of a <a> element.  Create an attributed string NSLinkAttributeName attribute
2500                 // for the range of the link.  Note that we create the attributed string from the DOM, which
2501                 // will have corrected any illegally nested <a> elements.
2502                 if (linkStartNode && n == linkStartNode) {
2503                     String href = parseURL(linkStartNode->getAttribute(hrefAttr));
2504                     KURL kURL = Mac(linkStartNode->document()->frame())->completeURL(href.deprecatedString());
2505                     
2506                     NSURL *URL = kURL.getNSURL();
2507                     NSRange tempRange = { linkStartLocation, [result length]-linkStartLocation }; // workaround for 4213314
2508                     [result addAttribute:NSLinkAttributeName value:URL range:tempRange];
2509                     linkStartNode = 0;
2510                 }
2511             }
2512             else if (n->hasTagName(olTag) || n->hasTagName(ulTag)) {
2513                 if (!hasNewLine)
2514                     text += '\n';
2515                 hasNewLine = true;
2516             } else if (n->hasTagName(liTag)) {
2517                 
2518                 int i, count = listItems.count();
2519                 for (i = 0; i < count; i++){
2520                     if (listItems.at(i) == n){
2521                         listItemLocations[i].end = [result length];
2522                         break;
2523                     }
2524                 }
2525                 if (!hasNewLine)
2526                     text += '\n';
2527                 hasNewLine = true;
2528             } else if (n->hasTagName(blockquoteTag) ||
2529                        n->hasTagName(ddTag) ||
2530                        n->hasTagName(divTag) ||
2531                        n->hasTagName(dlTag) ||
2532                        n->hasTagName(dtTag) ||
2533                        n->hasTagName(hrTag) ||
2534                        n->hasTagName(listingTag) ||
2535                        n->hasTagName(preTag) ||
2536                        n->hasTagName(tdTag) ||
2537                        n->hasTagName(thTag)) {
2538                 if (!hasNewLine)
2539                     text += '\n';
2540                 hasNewLine = true;
2541             } else if (n->hasTagName(pTag) ||
2542                        n->hasTagName(trTag) ||
2543                        n->hasTagName(h1Tag) ||
2544                        n->hasTagName(h2Tag) ||
2545                        n->hasTagName(h3Tag) ||
2546                        n->hasTagName(h4Tag) ||
2547                        n->hasTagName(h5Tag) ||
2548                        n->hasTagName(h6Tag)) {
2549                 if (!hasNewLine)
2550                     text += '\n';
2551                 // An extra newline is needed at the start, not the end, of these types of tags,
2552                 // so don't add another here.
2553                 hasNewLine = true;
2554             }
2555             
2556             NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()];
2557             [result appendAttributedString:partialString];
2558             [partialString release];
2559         }
2560
2561         n = next;
2562     }
2563     
2564     [pendingStyledSpace release];
2565     
2566     // Apply paragraph styles from outside in.  This ensures that nested lists correctly
2567     // override their parent's paragraph style.
2568     {
2569         unsigned i, count = listItems.count();
2570         Element *e;
2571
2572 #ifdef POSITION_LIST
2573         Node *containingBlock;
2574         int containingBlockX, containingBlockY;
2575         
2576         // Determine the position of the outermost containing block.  All paragraph
2577         // styles and tabs should be relative to this position.  So, the horizontal position of 
2578         // each item in the list (in the resulting attributed string) will be relative to position 
2579         // of the outermost containing block.
2580         if (count > 0){
2581             containingBlock = _startNode;
2582             while (containingBlock->renderer()->isInline()){
2583                 containingBlock = containingBlock->parentNode();
2584             }
2585             containingBlock->renderer()->absolutePosition(containingBlockX, containingBlockY);
2586         }
2587 #endif
2588         
2589         for (i = 0; i < count; i++){
2590             e = listItems.at(i);
2591             info = listItemLocations[i];
2592             
2593             if (info.end < info.start)
2594                 info.end = [result length];
2595                 
2596             RenderObject *r = e->renderer();
2597             RenderStyle *style = r->style();
2598
2599             int rx;
2600             NSFont *font = style->font().getNSFont();
2601             float pointSize = [font pointSize];
2602
2603 #ifdef POSITION_LIST
2604             int ry;
2605             r->absolutePosition(rx, ry);
2606             rx -= containingBlockX;
2607             
2608             // Ensure that the text is indented at least enough to allow for the markers.
2609             rx = MAX(rx, (int)maxMarkerWidth);
2610 #else
2611             rx = (int)MAX(maxMarkerWidth, pointSize);
2612 #endif
2613
2614             // The bullet text will be right aligned at the first tab marker, followed
2615             // by a space, followed by the list item text.  The space is arbitrarily
2616             // picked as pointSize*2/3.  The space on the first line of the text item
2617             // is established by a left aligned tab, on subsequent lines it's established
2618             // by the head indent.
2619             NSMutableParagraphStyle *mps = [[NSMutableParagraphStyle alloc] init];
2620             [mps setFirstLineHeadIndent: 0];
2621             [mps setHeadIndent: rx];
2622             [mps setTabStops:[NSArray arrayWithObjects:
2623                         [[[NSTextTab alloc] initWithType:NSRightTabStopType location:rx-(pointSize*2/3)] autorelease],
2624                         [[[NSTextTab alloc] initWithType:NSLeftTabStopType location:rx] autorelease],
2625                         nil]];
2626             NSRange tempRange = { info.start, info.end-info.start }; // workaround for 4213314
2627             [result addAttribute:NSParagraphStyleAttributeName value:mps range:tempRange];
2628             [mps release];
2629         }
2630     }
2631
2632     return result;
2633
2634     END_BLOCK_OBJC_EXCEPTIONS;
2635
2636     return nil;
2637 }
2638
2639 NSImage *FrameMac::imageFromRect(NSRect rect) const
2640 {
2641     NSView *view = d->m_view->getDocumentView();
2642     if (!view)
2643         return nil;
2644     
2645     NSImage *resultImage;
2646     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2647     
2648     NSRect bounds = [view bounds];
2649     resultImage = [[[NSImage alloc] initWithSize:rect.size] autorelease];
2650
2651     if (rect.size.width != 0 && rect.size.height != 0) {
2652         [resultImage setFlipped:YES];
2653         [resultImage lockFocus];
2654
2655         CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
2656
2657         CGContextSaveGState(context);
2658         CGContextTranslateCTM(context, bounds.origin.x - rect.origin.x, bounds.origin.y - rect.origin.y);
2659         [view drawRect:rect];
2660         CGContextRestoreGState(context);
2661
2662         [resultImage unlockFocus];
2663         [resultImage setFlipped:NO];
2664     }
2665
2666     return resultImage;
2667
2668     END_BLOCK_OBJC_EXCEPTIONS;
2669     
2670     return nil;
2671 }
2672
2673 NSImage *FrameMac::selectionImage() const
2674 {
2675     d->m_drawSelectionOnly = true;  // invoke special drawing mode
2676     NSImage *result = imageFromRect(visibleSelectionRect());
2677     d->m_drawSelectionOnly = false;
2678     return result;
2679 }
2680
2681 NSImage *FrameMac::snapshotDragImage(Node *node, NSRect *imageRect, NSRect *elementRect) const
2682 {
2683     RenderObject *renderer = node->renderer();
2684     if (!renderer)
2685         return nil;
2686     
2687     renderer->updateDragState(true);    // mark dragged nodes (so they pick up the right CSS)
2688     d->m_doc->updateLayout();        // forces style recalc - needed since changing the drag state might
2689                                         // imply new styles, plus JS could have changed other things
2690     IntRect topLevelRect;
2691     NSRect paintingRect = renderer->paintingRootRect(topLevelRect);
2692
2693     d->m_elementToDraw = node;              // invoke special sub-tree drawing mode
2694     NSImage *result = imageFromRect(paintingRect);
2695     renderer->updateDragState(false);
2696     d->m_doc->updateLayout();
2697     d->m_elementToDraw = 0;
2698
2699     if (elementRect)
2700         *elementRect = topLevelRect;
2701     if (imageRect)
2702         *imageRect = paintingRect;
2703     return result;
2704 }
2705
2706 NSFont *FrameMac::fontForSelection(bool *hasMultipleFonts) const
2707 {
2708     if (hasMultipleFonts)
2709         *hasMultipleFonts = false;
2710
2711     if (!d->m_selection.isRange()) {
2712         Node *nodeToRemove;
2713         RenderStyle *style = styleForSelectionStart(nodeToRemove); // sets nodeToRemove
2714
2715         NSFont *result = 0;
2716         if (style)
2717             result = style->font().getNSFont();
2718         
2719         if (nodeToRemove) {
2720             ExceptionCode ec;
2721             nodeToRemove->remove(ec);
2722             ASSERT(ec == 0);
2723         }
2724
2725         return result;
2726     }
2727
2728     NSFont *font = nil;
2729
2730     RefPtr<Range> range = d->m_selection.toRange();
2731     Node *startNode = range->editingStartPosition().node();
2732     if (startNode != nil) {
2733         Node *pastEnd = range->pastEndNode();
2734         // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one
2735         // unreproducible case where this didn't happen, so check for nil also.
2736         for (Node *n = startNode; n && n != pastEnd; n = n->traverseNextNode()) {
2737             RenderObject *renderer = n->renderer();
2738             if (!renderer)
2739                 continue;
2740             // FIXME: Are there any node types that have renderers, but that we should be skipping?
2741             NSFont *f = renderer->style()->font().getNSFont();
2742             if (!font) {
2743                 font = f;
2744                 if (!hasMultipleFonts)
2745                     break;
2746             } else if (font != f) {
2747                 *hasMultipleFonts = true;
2748                 break;
2749             }
2750         }
2751     }
2752
2753     return font;
2754 }
2755
2756 NSDictionary *FrameMac::fontAttributesForSelectionStart() const
2757 {
2758     Node *nodeToRemove;
2759     RenderStyle *style = styleForSelectionStart(nodeToRemove);
2760     if (!style)
2761         return nil;
2762
2763     NSMutableDictionary *result = [NSMutableDictionary dictionary];
2764
2765     if (style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0)
2766         [result setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2767
2768     if (style->font().getNSFont())
2769         [result setObject:style->font().getNSFont() forKey:NSFontAttributeName];
2770
2771     if (style->color().isValid() && style->color() != Color::black)
2772         [result setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2773
2774     ShadowData *shadow = style->textShadow();
2775     if (shadow) {
2776         NSShadow *s = [[NSShadow alloc] init];
2777         [s setShadowOffset:NSMakeSize(shadow->x, shadow->y)];
2778         [s setShadowBlurRadius:shadow->blur];
2779         [s setShadowColor:nsColor(shadow->color)];
2780         [result setObject:s forKey:NSShadowAttributeName];
2781     }
2782
2783     int decoration = style->textDecorationsInEffect();
2784     if (decoration & LINE_THROUGH)
2785         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSStrikethroughStyleAttributeName];
2786
2787     int superscriptInt = 0;
2788     switch (style->verticalAlign()) {
2789         case BASELINE:
2790         case BOTTOM:
2791         case BASELINE_MIDDLE:
2792         case LENGTH:
2793         case MIDDLE:
2794         case TEXT_BOTTOM:
2795         case TEXT_TOP:
2796         case TOP:
2797             break;
2798         case SUB:
2799             superscriptInt = -1;
2800             break;
2801         case SUPER:
2802             superscriptInt = 1;
2803             break;
2804     }
2805     if (superscriptInt)
2806         [result setObject:[NSNumber numberWithInt:superscriptInt] forKey:NSSuperscriptAttributeName];
2807
2808     if (decoration & UNDERLINE)
2809         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
2810
2811     if (nodeToRemove) {
2812         ExceptionCode ec = 0;
2813         nodeToRemove->remove(ec);
2814         ASSERT(ec == 0);
2815     }
2816
2817     return result;
2818 }
2819
2820 NSWritingDirection FrameMac::baseWritingDirectionForSelectionStart() const
2821 {
2822     NSWritingDirection result = NSWritingDirectionLeftToRight;
2823
2824     Position pos = VisiblePosition(d->m_selection.start(), d->m_selection.affinity()).deepEquivalent();
2825     Node *node = pos.node();
2826     if (!node || !node->renderer() || !node->renderer()->containingBlock())
2827         return result;
2828     RenderStyle *style = node->renderer()->containingBlock()->style();
2829     if (!style)
2830         return result;
2831         
2832     switch (style->direction()) {
2833         case LTR:
2834             result = NSWritingDirectionLeftToRight;
2835             break;
2836         case RTL:
2837             result = NSWritingDirectionRightToLeft;
2838             break;
2839     }
2840
2841     return result;
2842 }
2843
2844 void FrameMac::tokenizerProcessedData()
2845 {
2846     if (d->m_doc)
2847         checkCompleted();
2848     [_bridge tokenizerProcessedData];
2849 }
2850
2851 void FrameMac::setBridge(WebCoreFrameBridge *bridge)
2852
2853     if (_bridge == bridge)
2854         return;
2855
2856     KWQRetain(bridge);
2857     KWQRelease(_bridge);
2858     _bridge = bridge;
2859 }
2860
2861 String FrameMac::overrideMediaType() const
2862 {
2863     NSString *overrideType = [_bridge overrideMediaType];
2864     if (overrideType)
2865         return overrideType;
2866     return String();
2867 }
2868
2869 NSColor *FrameMac::bodyBackgroundColor() const
2870 {
2871     if (document() && document()->body() && document()->body()->renderer()) {
2872         Color bgColor = document()->body()->renderer()->style()->backgroundColor();
2873         if (bgColor.isValid())
2874             return nsColor(bgColor);
2875     }
2876     return nil;
2877 }
2878
2879 WebCoreKeyboardUIMode FrameMac::keyboardUIMode() const
2880 {
2881     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2882     return [_bridge keyboardUIMode];
2883     END_BLOCK_OBJC_EXCEPTIONS;
2884
2885     return WebCoreKeyboardAccessDefault;
2886 }
2887
2888 void FrameMac::didTellBridgeAboutLoad(const String& URL)
2889 {
2890     urlsBridgeKnowsAbout.add(URL.impl());
2891 }
2892
2893 bool FrameMac::haveToldBridgeAboutLoad(const String& URL)
2894 {
2895     return urlsBridgeKnowsAbout.contains(URL.impl());
2896 }
2897
2898 void FrameMac::clear()
2899 {
2900     urlsBridgeKnowsAbout.clear();
2901     setMarkedTextRange(0, nil, nil);
2902     Frame::clear();
2903 }
2904
2905 void FrameMac::print()
2906 {
2907     [Mac(this)->_bridge print];
2908 }
2909
2910 KJS::Bindings::Instance *FrameMac::getAppletInstanceForWidget(Widget *widget)
2911 {
2912     NSView *aView = widget->getView();
2913     if (!aView)
2914         return 0;
2915     jobject applet;
2916     
2917     // Get a pointer to the actual Java applet instance.
2918     if ([_bridge respondsToSelector:@selector(getAppletInView:)])
2919         applet = [_bridge getAppletInView:aView];
2920     else
2921         applet = [_bridge pollForAppletInView:aView];
2922     
2923     if (applet) {
2924         // Wrap the Java instance in a language neutral binding and hand
2925         // off ownership to the APPLET element.
2926         KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2927         KJS::Bindings::Instance *instance = KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::JavaLanguage, applet, executionContext);        
2928         return instance;
2929     }
2930     
2931     return 0;
2932 }
2933
2934 static KJS::Bindings::Instance *getInstanceForView(NSView *aView)
2935 {
2936     if ([aView respondsToSelector:@selector(objectForWebScript)]){
2937         id object = [aView objectForWebScript];
2938         if (object) {
2939             KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2940             return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::ObjectiveCLanguage, object, executionContext);
2941         }
2942     }
2943     else if ([aView respondsToSelector:@selector(pluginScriptableObject)]){
2944         void *object = [aView pluginScriptableObject];
2945         if (object) {
2946             KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2947             return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::CLanguage, object, executionContext);
2948         }
2949     }
2950     return 0;
2951 }
2952
2953 KJS::Bindings::Instance *FrameMac::getEmbedInstanceForWidget(Widget *widget)
2954 {
2955     return getInstanceForView(widget->getView());
2956 }
2957
2958 KJS::Bindings::Instance *FrameMac::getObjectInstanceForWidget(Widget *widget)
2959 {
2960     return getInstanceForView(widget->getView());
2961 }
2962
2963 void FrameMac::addPluginRootObject(KJS::Bindings::RootObject *root)
2964 {
2965     m_rootObjects.append(root);
2966 }
2967
2968 void FrameMac::cleanupPluginRootObjects()
2969 {
2970     JSLock lock;
2971
2972     unsigned count = m_rootObjects.size();
2973     for (unsigned i = 0; i < count; i++)
2974         m_rootObjects[i]->removeAllNativeReferences();
2975     m_rootObjects.clear();
2976 }
2977
2978 void FrameMac::registerCommandForUndoOrRedo(const EditCommandPtr &cmd, bool isRedo)
2979 {
2980     ASSERT(cmd.get());
2981     KWQEditCommand *kwq = [KWQEditCommand commandWithEditCommand:cmd.get()];
2982     NSUndoManager *undoManager = [_bridge undoManager];
2983     [undoManager registerUndoWithTarget:_bridge selector:(isRedo ? @selector(redoEditing:) : @selector(undoEditing:)) object:kwq];
2984     NSString *actionName = [_bridge nameForUndoAction:static_cast<WebUndoAction>(cmd.editingAction())];
2985     if (actionName)
2986         [undoManager setActionName:actionName];
2987     _haveUndoRedoOperations = YES;
2988 }
2989
2990 void FrameMac::registerCommandForUndo(const EditCommandPtr &cmd)
2991 {
2992     registerCommandForUndoOrRedo(cmd, NO);
2993 }
2994
2995 void FrameMac::registerCommandForRedo(const EditCommandPtr &cmd)
2996 {
2997     registerCommandForUndoOrRedo(cmd, YES);
2998 }
2999
3000 void FrameMac::clearUndoRedoOperations()
3001 {
3002     if (_haveUndoRedoOperations) {
3003         [[_bridge undoManager] removeAllActionsWithTarget:_bridge];
3004         _haveUndoRedoOperations = NO;
3005     }
3006 }
3007
3008 void FrameMac::issueUndoCommand()
3009 {
3010     if (canUndo())
3011         [[_bridge undoManager] undo];
3012 }
3013
3014 void FrameMac::issueRedoCommand()
3015 {
3016     if (canRedo())
3017         [[_bridge undoManager] redo];
3018 }
3019
3020 void FrameMac::issueCutCommand()
3021 {
3022     [_bridge issueCutCommand];
3023 }
3024
3025 void FrameMac::issueCopyCommand()
3026 {
3027     [_bridge issueCopyCommand];
3028 }
3029
3030 void FrameMac::issuePasteCommand()
3031 {
3032     [_bridge issuePasteCommand];
3033 }
3034
3035 void FrameMac::issuePasteAndMatchStyleCommand()
3036 {
3037     [_bridge issuePasteAndMatchStyleCommand];
3038 }
3039
3040 void FrameMac::issueTransposeCommand()
3041 {
3042     [_bridge issueTransposeCommand];
3043 }
3044
3045 bool FrameMac::canUndo() const
3046 {
3047     return [[Mac(this)->_bridge undoManager] canUndo];
3048 }
3049
3050 bool FrameMac::canRedo() const
3051 {
3052     return [[Mac(this)->_bridge undoManager] canRedo];
3053 }
3054
3055 bool FrameMac::canPaste() const
3056 {
3057     return [Mac(this)->_bridge canPaste];
3058 }
3059
3060 void FrameMac::markMisspellingsInAdjacentWords(const VisiblePosition &p)
3061 {
3062     if (![_bridge isContinuousSpellCheckingEnabled])
3063         return;
3064     markMisspellings(SelectionController(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)));
3065 }
3066
3067 void FrameMac::markMisspellings(const SelectionController &selection)
3068 {
3069     // This function is called with a selection already expanded to word boundaries.
3070     // Might be nice to assert that here.
3071
3072     if (![_bridge isContinuousSpellCheckingEnabled])
3073         return;
3074
3075     RefPtr<Range> searchRange(selection.toRange());
3076     if (!searchRange || searchRange->isDetached())
3077         return;
3078     
3079     // If we're not in an editable node, bail.
3080     int exception = 0;
3081     Node *editableNode = searchRange->startContainer(exception);
3082     if (!editableNode->isContentEditable())
3083         return;
3084     
3085     // Get the spell checker if it is available
3086     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
3087     if (checker == nil)
3088         return;
3089     
3090     WordAwareIterator it(searchRange.get());
3091     
3092     while (!it.atEnd()) {      // we may be starting at the end of the doc, and already by atEnd
3093         const UChar* chars = it.characters();
3094         int len = it.length();
3095         if (len > 1 || !QChar(chars[0]).isSpace()) {
3096             NSString *chunk = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(chars) length:len freeWhenDone:NO];
3097             int startIndex = 0;
3098             // Loop over the chunk to find each misspelling in it.
3099             while (startIndex < len) {
3100                 NSRange misspelling = [checker checkSpellingOfString:chunk startingAt:startIndex language:nil wrap:NO inSpellDocumentWithTag:[_bridge spellCheckerDocumentTag] wordCount:NULL];
3101                 if (misspelling.length == 0)
3102                     break;
3103                 else {
3104                     // Build up result range and string.  Note the misspelling may span many text nodes,
3105                     // but the CharIterator insulates us from this complexity
3106                     RefPtr<Range> misspellingRange(rangeOfContents(document()));
3107                     CharacterIterator chars(it.range().get());
3108                     chars.advance(misspelling.location);
3109                     misspellingRange->setStart(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
3110                     chars.advance(misspelling.length);
3111                     misspellingRange->setEnd(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
3112                     // Mark misspelling in document.
3113                     document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
3114                     startIndex = misspelling.location + misspelling.length;
3115                 }
3116             }
3117             [chunk release];
3118         }
3119     
3120         it.advance();
3121     }
3122 }
3123
3124 void FrameMac::respondToChangedSelection(const SelectionController &oldSelection, bool closeTyping)
3125 {
3126     if (document()) {
3127         if ([_bridge isContinuousSpellCheckingEnabled]) {
3128             SelectionController oldAdjacentWords = SelectionController();
3129             
3130             // If this is a change in selection resulting from a delete operation, oldSelection may no longer
3131             // be in the document.
3132             if (oldSelection.start().node() && oldSelection.start().node()->inDocument()) {
3133                 VisiblePosition oldStart(oldSelection.start(), oldSelection.affinity());
3134                 oldAdjacentWords = SelectionController(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));   
3135             }
3136
3137             VisiblePosition newStart(selection().start(), selection().affinity());
3138             SelectionController newAdjacentWords(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
3139
3140             if (oldAdjacentWords != newAdjacentWords) {
3141                 // Mark misspellings in the portion that was previously unmarked because of
3142                 // the proximity of the start of the selection. We only spell check words in
3143                 // the vicinity of the start of the old selection because the spelling checker
3144                 // is not fast enough to do a lot of spelling checking implicitly. This matches
3145                 // AppKit. This function is really the only code that knows that rule. The
3146                 // markMisspellings function is prepared to handler larger ranges.
3147
3148                 // When typing we check spelling elsewhere, so don't redo it here.
3149                 if (closeTyping)
3150                     markMisspellings(oldAdjacentWords);
3151
3152                 // This only erases a marker in the first word of the selection.
3153                 // Perhaps peculiar, but it matches AppKit.
3154                 document()->removeMarkers(newAdjacentWords.toRange().get(), DocumentMarker::Spelling);
3155             }
3156         } else
3157             // When continuous spell checking is off, no markers appear after the selection changes.
3158             document()->removeMarkers(DocumentMarker::Spelling);
3159     }
3160
3161     [_bridge respondToChangedSelection];
3162 }
3163
3164 bool FrameMac::shouldChangeSelection(const SelectionController &oldSelection, const SelectionController &newSelection, EAffinity affinity, bool stillSelecting) const
3165 {
3166     return [_bridge shouldChangeSelectedDOMRange:[DOMRange _rangeWith:oldSelection.toRange().get()]
3167                                       toDOMRange:[DOMRange _rangeWith:newSelection.toRange().get()]
3168                                         affinity:static_cast<NSSelectionAffinity>(affinity)
3169                                   stillSelecting:stillSelecting];
3170 }
3171
3172 void FrameMac::respondToChangedContents()
3173 {
3174     if (AccessibilityObjectCache::accessibilityEnabled())
3175         renderer()->document()->getAccObjectCache()->postNotificationToTopWebArea(renderer(), "AXValueChanged");
3176     [_bridge respondToChangedContents];
3177 }
3178
3179 bool FrameMac::isContentEditable() const
3180 {
3181     return Frame::isContentEditable() || [_bridge isEditable];
3182 }
3183
3184 bool FrameMac::shouldBeginEditing(const Range *range) const
3185 {
3186     ASSERT(range);
3187     return [_bridge shouldBeginEditing:[DOMRange _rangeWith:const_cast<Range*>(range)]];
3188 }
3189
3190 bool FrameMac::shouldEndEditing(const Range *range) const
3191 {
3192     ASSERT(range);
3193     return [_bridge shouldEndEditing:[DOMRange _rangeWith:const_cast<Range*>(range)]];
3194 }
3195
3196 void FrameMac::didBeginEditing() const
3197 {
3198     [_bridge didBeginEditing];
3199 }
3200
3201 void FrameMac::didEndEditing() const
3202 {
3203     [_bridge didEndEditing];
3204 }
3205
3206 void FrameMac::textFieldDidBeginEditing(Element* input)
3207 {
3208     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3209     [_bridge textFieldDidBeginEditing:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
3210     END_BLOCK_OBJC_EXCEPTIONS;
3211 }
3212
3213 void FrameMac::textFieldDidEndEditing(Element* input)
3214 {
3215     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3216     [_bridge textFieldDidEndEditing:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
3217     END_BLOCK_OBJC_EXCEPTIONS;
3218 }
3219
3220 void FrameMac::textDidChangeInTextField(Element* input)
3221 {
3222     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3223     [_bridge textDidChangeInTextField:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
3224     END_BLOCK_OBJC_EXCEPTIONS;
3225 }
3226
3227 void FrameMac::textDidChangeInTextArea(Element* textarea)
3228 {
3229     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3230     [_bridge textDidChangeInTextArea:(DOMHTMLTextAreaElement *)[DOMElement _elementWith:textarea]];
3231     END_BLOCK_OBJC_EXCEPTIONS;
3232 }
3233
3234 bool FrameMac::doTextFieldCommandFromEvent(Element* input, const PlatformKeyboardEvent* event)
3235 {
3236     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3237     return [_bridge textField:(DOMHTMLInputElement *)[DOMElement _elementWith:input] doCommandBySelector:selectorForKeyEvent(event)];
3238     END_BLOCK_OBJC_EXCEPTIONS;
3239     return false;
3240 }
3241
3242 void FrameMac::textWillBeDeletedInTextField(Element* input)
3243 {
3244     // We're using the deleteBackward selector for all deletion operations since the autofill code treats all deletions the same way.
3245     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3246     [_bridge textField:(DOMHTMLInputElement *)[DOMElement _elementWith:input] doCommandBySelector:@selector(deleteBackward:)];
3247     END_BLOCK_OBJC_EXCEPTIONS;
3248 }
3249
3250 static DeprecatedValueList<MarkedTextUnderline> convertAttributesToUnderlines(const Range *markedTextRange, NSArray *attributes, NSArray *ranges)
3251 {
3252     DeprecatedValueList<MarkedTextUnderline> result;
3253
3254     int exception = 0;
3255     int baseOffset = markedTextRange->startOffset(exception);
3256
3257     unsigned length = [attributes count];
3258     ASSERT([ranges count] == length);
3259
3260     for (unsigned i = 0; i < length; i++) {
3261         NSNumber *style = [[attributes objectAtIndex:i] objectForKey:NSUnderlineStyleAttributeName];
3262         if (!style)
3263             continue;
3264         NSRange range = [[ranges objectAtIndex:i] rangeValue];
3265         NSColor *color = [[attributes objectAtIndex:i] objectForKey:NSUnderlineColorAttributeName];
3266         Color qColor = Color::black;
3267         if (color) {
3268             NSColor* deviceColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
3269             qColor = Color(makeRGBA((int)(255 * [deviceColor redComponent]),
3270                                     (int)(255 * [deviceColor blueComponent]),
3271                                     (int)(255 * [deviceColor greenComponent]),
3272                                     (int)(255 * [deviceColor alphaComponent])));
3273         }
3274
3275         result.append(MarkedTextUnderline(range.location + baseOffset, 
3276                                           range.location + baseOffset + range.length, 
3277                                           qColor,
3278                                           [style intValue] > 1));
3279     }
3280
3281     return result;
3282 }
3283
3284 void FrameMac::setMarkedTextRange(const Range *range, NSArray *attributes, NSArray *ranges)
3285 {
3286     int exception = 0;
3287
3288     ASSERT(!range || range->startContainer(exception) == range->endContainer(exception));
3289     ASSERT(!range || range->collapsed(exception) || range->startContainer(exception)->isTextNode());
3290
3291     if (attributes == nil) {
3292         d->m_markedTextUsesUnderlines = false;
3293         d->m_markedTextUnderlines.clear();
3294     } else {
3295         d->m_markedTextUsesUnderlines = true;
3296         d->m_markedTextUnderlines = convertAttributesToUnderlines(range, attributes, ranges);
3297     }
3298
3299     if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
3300         m_markedTextRange->startContainer(exception)->renderer()->repaint();
3301
3302     if (range && range->collapsed(exception))
3303         m_markedTextRange = 0;
3304     else
3305         m_markedTextRange = const_cast<Range*>(range);
3306
3307     if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
3308         m_markedTextRange->startContainer(exception)->renderer()->repaint();
3309 }
3310
3311 bool FrameMac::canGoBackOrForward(int distance) const
3312 {
3313     return [_bridge canGoBackOrForward:distance];
3314 }
3315
3316 void FrameMac::didFirstLayout()
3317 {
3318     [_bridge didFirstLayout];
3319 }
3320
3321 NSMutableDictionary *FrameMac::dashboardRegionsDictionary()
3322 {
3323     Document *doc = document();
3324     if (!doc)
3325         return nil;
3326
3327     const DeprecatedValueList<DashboardRegionValue> regions = doc->dashboardRegions();
3328     unsigned i, count = regions.count();
3329
3330     // Convert the DeprecatedValueList<DashboardRegionValue> into a NSDictionary of WebDashboardRegions
3331     NSMutableDictionary *webRegions = [[[NSMutableDictionary alloc] initWithCapacity:count] autorelease];
3332     for (i = 0; i < count; i++) {
3333         DashboardRegionValue region = regions[i];
3334
3335         if (region.type == StyleDashboardRegion::None)
3336             continue;
3337         
3338         NSString *label = region.label;
3339         WebDashboardRegionType type = WebDashboardRegionTypeNone;
3340         if (region.type == StyleDashboardRegion::Circle)
3341             type = WebDashboardRegionTypeCircle;
3342         else if (region.type == StyleDashboardRegion::Rectangle)
3343             type = WebDashboardRegionTypeRectangle;
3344         NSMutableArray *regionValues = [webRegions objectForKey:label];
3345         if (!regionValues) {
3346             regionValues = [NSMutableArray array];
3347             [webRegions setObject:regionValues forKey:label];
3348         }
3349         
3350         WebDashboardRegion *webRegion = [[[WebDashboardRegion alloc] initWithRect:region.bounds clip:region.clip type:type] autorelease];
3351         [regionValues addObject:webRegion];
3352     }
3353     
3354     return webRegions;
3355 }
3356
3357 void FrameMac::dashboardRegionsChanged()
3358 {
3359     NSMutableDictionary *webRegions = dashboardRegionsDictionary();
3360     [_bridge dashboardRegionsChanged:webRegions];
3361 }
3362
3363 bool FrameMac::isCharacterSmartReplaceExempt(const QChar &c, bool isPreviousChar)
3364 {
3365     return [_bridge isCharacterSmartReplaceExempt:c.unicode() isPreviousCharacter:isPreviousChar];
3366 }
3367
3368 void FrameMac::handledOnloadEvents()
3369 {
3370     [_bridge handledOnloadEvents];
3371 }
3372
3373 bool FrameMac::shouldClose()
3374 {
3375     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3376
3377     if (![_bridge canRunBeforeUnloadConfirmPanel])
3378         return true;
3379
3380     RefPtr<Document> doc = document();
3381     if (!doc)
3382         return true;
3383     HTMLElement* body = doc->body();
3384     if (!body)
3385         return true;
3386
3387     RefPtr<BeforeUnloadEvent> event = new BeforeUnloadEvent;
3388     event->setTarget(doc.get());
3389     doc->handleWindowEvent(event.get(), false);
3390
3391     if (!event->defaultPrevented() && doc)
3392         doc->defaultEventHandler(event.get());
3393     if (event->result().isNull())
3394         return true;
3395
3396     String text = event->result();
3397     text.replace('\\', backslashAsCurrencySymbol());
3398
3399     return [_bridge runBeforeUnloadConfirmPanelWithMessage:text];
3400
3401     END_BLOCK_OBJC_EXCEPTIONS;
3402
3403     return true;
3404 }
3405
3406 void FrameMac::dragSourceMovedTo(const PlatformMouseEvent& event)
3407 {
3408     if (_dragSrc && _dragSrcMayBeDHTML)
3409         // for now we don't care if event handler cancels default behavior, since there is none
3410         dispatchDragSrcEvent(dragEvent, event);
3411 }
3412
3413 void FrameMac::dragSourceEndedAt(const PlatformMouseEvent& event, NSDragOperation operation)
3414 {
3415     if (_dragSrc && _dragSrcMayBeDHTML) {
3416         _dragClipboard->setDestinationOperation(operation);
3417         // for now we don't care if event handler cancels default behavior, since there is none
3418         dispatchDragSrcEvent(dragendEvent, event);
3419     }
3420     freeClipboard();
3421     _dragSrc = 0;
3422 }
3423
3424 // returns if we should continue "default processing", i.e., whether eventhandler canceled
3425 bool FrameMac::dispatchDragSrcEvent(const AtomicString &eventType, const PlatformMouseEvent& event) const
3426 {
3427     bool noDefaultProc = d->m_view->dispatchDragEvent(eventType, _dragSrc.get(), event, _dragClipboard.get());
3428     return !noDefaultProc;
3429 }
3430
3431 void Frame::setNeedsReapplyStyles()
3432 {
3433     [Mac(this)->bridge() setNeedsReapplyStyles];
3434 }
3435
3436 FloatRect FrameMac::customHighlightLineRect(const AtomicString& type, const FloatRect& lineRect)
3437 {
3438     return [bridge() customHighlightRect:type forLine:lineRect];
3439 }
3440
3441 void FrameMac::paintCustomHighlight(const AtomicString& type, const FloatRect& boxRect, const FloatRect& lineRect, bool text, bool line)
3442 {
3443     [bridge() paintCustomHighlight:type forBox:boxRect onLine:lineRect behindText:text entireLine:line];
3444 }
3445
3446 }