Reviewed by Adele.
[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 "BlockExceptions.h"
31 #import "BrowserExtensionMac.h"
32 #import "CSSComputedStyleDeclaration.h"
33 #import "Cache.h"
34 #import "Cursor.h"
35 #import "DOMInternal.h"
36 #import "DOMWindow.h"
37 #import "EventNames.h"
38 #import "FloatRect.h"
39 #import "FoundationExtras.h"
40 #import "FramePrivate.h"
41 #import "GraphicsContext.h"
42 #import "HTMLDocument.h"
43 #import "HTMLFormElement.h"
44 #import "HTMLGenericFormElement.h"
45 #import "KWQEditCommand.h"
46 #import "KWQFormData.h"
47 #import "KWQPageState.h"
48 #import "Logging.h"
49 #import "MouseEventWithHitTestResults.h"
50 #import "PlatformKeyboardEvent.h"
51 #import "PlatformWheelEvent.h"
52 #import "Plugin.h"
53 #import "RegularExpression.h"
54 #import "RenderCanvas.h"
55 #import "RenderImage.h"
56 #import "RenderTableCell.h"
57 #import "RenderTheme.h"
58 #import "TextIterator.h"
59 #import "TransferJob.h"
60 #import "WebCoreFrameBridge.h"
61 #import "WebCoreViewFactory.h"
62 #import "WebDashboardRegion.h"
63 #import "csshelper.h"
64 #import "html_tableimpl.h"
65 #import "kjs_window.h"
66 #import "render_frames.h"
67 #import "render_list.h"
68 #import "visible_units.h"
69 #import <JavaScriptCore/NP_jsobject.h>
70 #import <JavaScriptCore/WebScriptObjectPrivate.h>
71 #import <JavaScriptCore/npruntime_impl.h>
72
73 #undef _KWQ_TIMING
74
75 @interface NSObject (WebPlugIn)
76 - (id)objectForWebScript;
77 - (void *)pluginScriptableObject;
78 @end
79
80 using namespace std;
81 using namespace KJS::Bindings;
82 using namespace KIO;
83
84 using KJS::JSLock;
85 using KJS::PausedTimeouts;
86 using KJS::SavedBuiltins;
87 using KJS::SavedProperties;
88
89 namespace WebCore {
90
91 using namespace EventNames;
92 using namespace HTMLNames;
93
94 NSEvent* FrameMac::_currentEvent = nil;
95
96 static NSMutableDictionary* createNSDictionary(const HashMap<String, String>& map)
97 {
98     NSMutableDictionary* dict = [[NSMutableDictionary alloc] initWithCapacity:map.size()];
99     HashMap<String, String>::const_iterator end = map.end();
100     for (HashMap<String, String>::const_iterator it = map.begin(); it != end; ++it) {
101         NSString* key = it->first;
102         NSString* object = it->second;
103         [dict setObject:object forKey:key];
104     }
105     return dict;
106 }
107
108 static const unsigned int escChar = 27;
109 static SEL selectorForKeyEvent(const PlatformKeyboardEvent* event)
110 {
111     // FIXME: This helper function is for the autofill code so the bridge can pass a selector to the form delegate.  
112     // Eventually, we should move all of the autofill code down to WebKit and remove the need for this function by
113     // not relying on the selector in the new implementation.
114     String key = event->unmodifiedText();
115     if (key.length() != 1)
116         return 0;
117
118     SEL selector = NULL;
119     switch (key[0U]) {
120     case NSUpArrowFunctionKey:
121         selector = @selector(moveUp:); break;
122     case NSDownArrowFunctionKey:
123         selector = @selector(moveDown:); break;
124     case escChar:
125         selector = @selector(cancel:); break;
126     case NSTabCharacter:
127         selector = @selector(insertTab:); break;
128     case NSBackTabCharacter:
129         selector = @selector(insertBacktab:); break;
130     case NSNewlineCharacter:
131     case NSCarriageReturnCharacter:
132     case NSEnterCharacter:
133         selector = @selector(insertNewline:); break;
134         break;
135     }
136     return selector;
137 }
138
139
140 bool FrameView::isFrameView() const
141 {
142     return true;
143 }
144
145 FrameMac::FrameMac(Page* page, RenderPart* ownerRenderer)
146     : Frame(page, ownerRenderer)
147     , _bridge(nil)
148     , _mouseDownView(nil)
149     , _sendingEventToSubview(false)
150     , _mouseDownMayStartDrag(false)
151     , _mouseDownMayStartSelect(false)
152     , _activationEventNumber(0)
153     , _bindingRoot(0)
154     , _windowScriptObject(0)
155     , _windowScriptNPObject(0)
156 {
157     d->m_extension = new BrowserExtensionMac(this);
158 }
159
160 FrameMac::~FrameMac()
161 {
162     setView(0);
163     freeClipboard();
164     clearRecordedFormValues();    
165     
166     [_bridge clearFrame];
167     KWQRelease(_bridge);
168     _bridge = nil;
169 }
170
171 void FrameMac::freeClipboard()
172 {
173     if (_dragClipboard)
174         _dragClipboard->setAccessPolicy(ClipboardMac::Numb);
175 }
176
177 bool FrameMac::openURL(const KURL &url)
178 {
179     BEGIN_BLOCK_OBJC_EXCEPTIONS;
180
181     // FIXME: The lack of args here to get the reload flag from
182     // indicates a problem in how we use Frame::processObjectRequest,
183     // where we are opening the URL before the args are set up.
184     [_bridge loadURL:url.getNSURL()
185             referrer:[_bridge referrer]
186               reload:NO
187          userGesture:userGestureHint()
188               target:nil
189      triggeringEvent:nil
190                 form:nil
191           formValues:nil];
192
193     END_BLOCK_OBJC_EXCEPTIONS;
194
195     return true;
196 }
197
198 void FrameMac::openURLRequest(const ResourceRequest& request)
199 {
200     BEGIN_BLOCK_OBJC_EXCEPTIONS;
201
202     NSString *referrer;
203     String argsReferrer = request.referrer();
204     if (!argsReferrer.isEmpty())
205         referrer = argsReferrer;
206     else
207         referrer = [_bridge referrer];
208
209     [_bridge loadURL:request.url().getNSURL()
210             referrer:referrer
211               reload:request.reload
212          userGesture:userGestureHint()
213               target:request.frameName
214      triggeringEvent:nil
215                 form:nil
216           formValues:nil];
217
218     END_BLOCK_OBJC_EXCEPTIONS;
219 }
220
221
222 // Either get cached regexp or build one that matches any of the labels.
223 // The regexp we build is of the form:  (STR1|STR2|STRN)
224 RegularExpression *regExpForLabels(NSArray *labels)
225 {
226     // All the ObjC calls in this method are simple array and string
227     // calls which we can assume do not raise exceptions
228
229
230     // Parallel arrays that we use to cache regExps.  In practice the number of expressions
231     // that the app will use is equal to the number of locales is used in searching.
232     static const unsigned int regExpCacheSize = 4;
233     static NSMutableArray *regExpLabels = nil;
234     static Vector<RegularExpression*> regExps;
235     static RegularExpression wordRegExp = RegularExpression("\\w");
236
237     RegularExpression *result;
238     if (!regExpLabels)
239         regExpLabels = [[NSMutableArray alloc] initWithCapacity:regExpCacheSize];
240     CFIndex cacheHit = [regExpLabels indexOfObject:labels];
241     if (cacheHit != NSNotFound)
242         result = regExps.at(cacheHit);
243     else {
244         DeprecatedString pattern("(");
245         unsigned int numLabels = [labels count];
246         unsigned int i;
247         for (i = 0; i < numLabels; i++) {
248             DeprecatedString label = DeprecatedString::fromNSString((NSString *)[labels objectAtIndex:i]);
249
250             bool startsWithWordChar = false;
251             bool endsWithWordChar = false;
252             if (label.length() != 0) {
253                 startsWithWordChar = wordRegExp.search(label.at(0)) >= 0;
254                 endsWithWordChar = wordRegExp.search(label.at(label.length() - 1)) >= 0;
255             }
256             
257             if (i != 0)
258                 pattern.append("|");
259             // Search for word boundaries only if label starts/ends with "word characters".
260             // If we always searched for word boundaries, this wouldn't work for languages
261             // such as Japanese.
262             if (startsWithWordChar) {
263                 pattern.append("\\b");
264             }
265             pattern.append(label);
266             if (endsWithWordChar) {
267                 pattern.append("\\b");
268             }
269         }
270         pattern.append(")");
271         result = new RegularExpression(pattern, false);
272     }
273
274     // add regexp to the cache, making sure it is at the front for LRU ordering
275     if (cacheHit != 0) {
276         if (cacheHit != NSNotFound) {
277             // remove from old spot
278             [regExpLabels removeObjectAtIndex:cacheHit];
279             regExps.remove(cacheHit);
280         }
281         // add to start
282         [regExpLabels insertObject:labels atIndex:0];
283         regExps.insert(0, result);
284         // trim if too big
285         if ([regExpLabels count] > regExpCacheSize) {
286             [regExpLabels removeObjectAtIndex:regExpCacheSize];
287             RegularExpression *last = regExps.last();
288             regExps.removeLast();
289             delete last;
290         }
291     }
292     return result;
293 }
294
295 NSString *FrameMac::searchForLabelsAboveCell(RegularExpression *regExp, HTMLTableCellElement *cell)
296 {
297     RenderTableCell *cellRenderer = static_cast<RenderTableCell *>(cell->renderer());
298
299     if (cellRenderer && cellRenderer->isTableCell()) {
300         RenderTableCell *cellAboveRenderer = cellRenderer->table()->cellAbove(cellRenderer);
301
302         if (cellAboveRenderer) {
303             HTMLTableCellElement *aboveCell =
304                 static_cast<HTMLTableCellElement *>(cellAboveRenderer->element());
305
306             if (aboveCell) {
307                 // search within the above cell we found for a match
308                 for (Node *n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) {
309                     if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
310                         // For each text chunk, run the regexp
311                         DeprecatedString nodeString = n->nodeValue().deprecatedString();
312                         int pos = regExp->searchRev(nodeString);
313                         if (pos >= 0)
314                             return nodeString.mid(pos, regExp->matchedLength()).getNSString();
315                     }
316                 }
317             }
318         }
319     }
320     // Any reason in practice to search all cells in that are above cell?
321     return nil;
322 }
323
324 NSString *FrameMac::searchForLabelsBeforeElement(NSArray *labels, Element *element)
325 {
326     RegularExpression *regExp = regExpForLabels(labels);
327     // We stop searching after we've seen this many chars
328     const unsigned int charsSearchedThreshold = 500;
329     // This is the absolute max we search.  We allow a little more slop than
330     // charsSearchedThreshold, to make it more likely that we'll search whole nodes.
331     const unsigned int maxCharsSearched = 600;
332     // If the starting element is within a table, the cell that contains it
333     HTMLTableCellElement *startingTableCell = 0;
334     bool searchedCellAbove = false;
335
336     // walk backwards in the node tree, until another element, or form, or end of tree
337     int unsigned lengthSearched = 0;
338     Node *n;
339     for (n = element->traversePreviousNode();
340          n && lengthSearched < charsSearchedThreshold;
341          n = n->traversePreviousNode())
342     {
343         if (n->hasTagName(formTag)
344             || (n->isHTMLElement()
345                 && static_cast<HTMLElement *>(n)->isGenericFormElement()))
346         {
347             // We hit another form element or the start of the form - bail out
348             break;
349         } else if (n->hasTagName(tdTag) && !startingTableCell) {
350             startingTableCell = static_cast<HTMLTableCellElement *>(n);
351         } else if (n->hasTagName(trTag) && startingTableCell) {
352             NSString *result = searchForLabelsAboveCell(regExp, startingTableCell);
353             if (result) {
354                 return result;
355             }
356             searchedCellAbove = true;
357         } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
358             // For each text chunk, run the regexp
359             DeprecatedString nodeString = n->nodeValue().deprecatedString();
360             // add 100 for slop, to make it more likely that we'll search whole nodes
361             if (lengthSearched + nodeString.length() > maxCharsSearched)
362                 nodeString = nodeString.right(charsSearchedThreshold - lengthSearched);
363             int pos = regExp->searchRev(nodeString);
364             if (pos >= 0)
365                 return nodeString.mid(pos, regExp->matchedLength()).getNSString();
366             else
367                 lengthSearched += nodeString.length();
368         }
369     }
370
371     // If we started in a cell, but bailed because we found the start of the form or the
372     // previous element, we still might need to search the row above us for a label.
373     if (startingTableCell && !searchedCellAbove) {
374          return searchForLabelsAboveCell(regExp, startingTableCell);
375     } else {
376         return nil;
377     }
378 }
379
380 NSString *FrameMac::matchLabelsAgainstElement(NSArray *labels, Element *element)
381 {
382     DeprecatedString name = element->getAttribute(nameAttr).deprecatedString();
383     // Make numbers and _'s in field names behave like word boundaries, e.g., "address2"
384     name.replace(RegularExpression("[[:digit:]]"), " ");
385     name.replace('_', ' ');
386     
387     RegularExpression *regExp = regExpForLabels(labels);
388     // Use the largest match we can find in the whole name string
389     int pos;
390     int length;
391     int bestPos = -1;
392     int bestLength = -1;
393     int start = 0;
394     do {
395         pos = regExp->search(name, start);
396         if (pos != -1) {
397             length = regExp->matchedLength();
398             if (length >= bestLength) {
399                 bestPos = pos;
400                 bestLength = length;
401             }
402             start = pos+1;
403         }
404     } while (pos != -1);
405
406     if (bestPos != -1)
407         return name.mid(bestPos, bestLength).getNSString();
408     return nil;
409 }
410
411 // Searches from the beginning of the document if nothing is selected.
412 bool FrameMac::findString(NSString *string, bool forward, bool caseFlag, bool wrapFlag)
413 {
414     String target = string;
415     if (target.isEmpty())
416         return false;
417     
418     // Initially search from the start (if forward) or end (if backward) of the selection, and search to edge of document.
419     RefPtr<Range> searchRange(rangeOfContents(document()));
420     if (selection().start().node()) {
421         if (forward)
422             setStart(searchRange.get(), VisiblePosition(selection().start(), selection().affinity()));
423         else
424             setEnd(searchRange.get(), VisiblePosition(selection().end(), selection().affinity()));
425     }
426     RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, forward, caseFlag));
427     
428     // If we re-found the (non-empty) selected range, then search again starting just past the selected range.
429     if (selection().start().node() && *resultRange == *selection().toRange()) {
430         searchRange = rangeOfContents(document());
431         if (forward)
432             setStart(searchRange.get(), VisiblePosition(selection().end(), selection().affinity()));
433         else
434             setEnd(searchRange.get(), VisiblePosition(selection().start(), selection().affinity()));
435         resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
436     }
437     
438     int exception = 0;
439     
440     // if we didn't find anything and we're wrapping, search again in the entire document (this will
441     // redundantly re-search the area already searched in some cases).
442     if (resultRange->collapsed(exception) && wrapFlag) {
443         searchRange = rangeOfContents(document());
444         resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
445         // We used to return false here if we ended up with the same range that we started with
446         // (e.g., the selection was already the only instance of this text). But we decided that
447         // this should be a success case instead, so we'll just fall through in that case.
448     }
449
450     if (resultRange->collapsed(exception))
451         return false;
452
453     setSelection(SelectionController(resultRange.get(), DOWNSTREAM));
454     revealSelection();
455     return true;
456 }
457
458 void FrameMac::submitForm(const ResourceRequest& request)
459 {
460     BEGIN_BLOCK_OBJC_EXCEPTIONS;
461
462     // FIXME: We'd like to remove this altogether and fix the multiple form submission issue another way.
463     // We do not want to submit more than one form from the same page,
464     // nor do we want to submit a single form more than once.
465     // This flag prevents these from happening; not sure how other browsers prevent this.
466     // The flag is reset in each time we start handle a new mouse or key down event, and
467     // also in setView since this part may get reused for a page from the back/forward cache.
468     // The form multi-submit logic here is only needed when we are submitting a form that affects this frame.
469     // FIXME: Frame targeting is only one of the ways the submission could end up doing something other
470     // than replacing this frame's content, so this check is flawed. On the other hand, the check is hardly
471     // needed any more now that we reset d->m_submittedFormURL on each mouse or key down event.
472     WebCoreFrameBridge *target = request.frameName.isEmpty() ? _bridge : [_bridge findFrameNamed:request.frameName];
473     Frame *targetPart = [target impl];
474     bool willReplaceThisFrame = false;
475     for (Frame *p = this; p; p = p->tree()->parent()) {
476         if (p == targetPart) {
477             willReplaceThisFrame = true;
478             break;
479         }
480     }
481     if (willReplaceThisFrame) {
482         if (d->m_submittedFormURL == request.url())
483             return;
484         d->m_submittedFormURL = request.url();
485     }
486
487     ObjCDOMElement* submitForm = [DOMElement _elementWith:d->m_formAboutToBeSubmitted.get()];
488     NSMutableDictionary* formValues = createNSDictionary(d->m_formValuesAboutToBeSubmitted);
489     
490     if (!request.doPost()) {
491         [_bridge loadURL:request.url().getNSURL()
492                 referrer:[_bridge referrer] 
493                   reload:request.reload
494              userGesture:true
495                   target:request.frameName
496          triggeringEvent:_currentEvent
497                     form:submitForm
498               formValues:formValues];
499     } else {
500         ASSERT(request.contentType().startsWith("Content-Type: "));
501         [_bridge postWithURL:request.url().getNSURL()
502                     referrer:[_bridge referrer] 
503                       target:request.frameName
504                         data:arrayFromFormData(request.postData)
505                  contentType:request.contentType().substring(14)
506              triggeringEvent:_currentEvent
507                         form:submitForm
508                   formValues:formValues];
509     }
510     [formValues release];
511     clearRecordedFormValues();
512
513     END_BLOCK_OBJC_EXCEPTIONS;
514 }
515
516 void FrameMac::frameDetached()
517 {
518     Frame::frameDetached();
519
520     BEGIN_BLOCK_OBJC_EXCEPTIONS;
521     [Mac(this)->bridge() frameDetached];
522     END_BLOCK_OBJC_EXCEPTIONS;
523 }
524
525 void FrameMac::urlSelected(const ResourceRequest& request)
526 {
527     BEGIN_BLOCK_OBJC_EXCEPTIONS;
528
529     NSString* referrer;
530     String argsReferrer = request.referrer();
531     if (!argsReferrer.isEmpty())
532         referrer = argsReferrer;
533     else
534         referrer = [_bridge referrer];
535
536     [_bridge loadURL:request.url().getNSURL()
537             referrer:referrer
538               reload:request.reload
539          userGesture:true
540               target:request.frameName
541      triggeringEvent:_currentEvent
542                 form:nil
543           formValues:nil];
544
545     END_BLOCK_OBJC_EXCEPTIONS;
546 }
547
548 ObjectContentType FrameMac::objectContentType(const KURL& url, const String& mimeType)
549 {
550     return (ObjectContentType)[_bridge determineObjectFromMIMEType:mimeType URL:url.getNSURL()];
551 }
552
553 static NSArray* nsArray(const Vector<String>& vector)
554 {
555     unsigned len = vector.size();
556     NSMutableArray* array = [NSMutableArray arrayWithCapacity:len];
557     for (unsigned x = 0; x < len; x++)
558         [array addObject:vector[x]];
559     return array;
560 }
561
562 Plugin* FrameMac::createPlugin(Element* element, const KURL& url, const Vector<String>& paramNames, const Vector<String>& paramValues, const String& mimeType)
563 {
564     BEGIN_BLOCK_OBJC_EXCEPTIONS;
565
566     return new Plugin(new Widget([_bridge viewForPluginWithURL:url.getNSURL()
567                                   attributeNames:nsArray(paramNames)
568                                   attributeValues:nsArray(paramValues)
569                                   MIMEType:mimeType
570                                   DOMElement:(element ? [DOMElement _elementWith:element] : nil)]));
571
572     END_BLOCK_OBJC_EXCEPTIONS;
573     return 0;
574 }
575
576
577 Frame* FrameMac::createFrame(const KURL& url, const String& name, RenderPart* renderer, const String& referrer)
578 {
579     BEGIN_BLOCK_OBJC_EXCEPTIONS;
580     
581     BOOL allowsScrolling = YES;
582     int marginWidth = -1;
583     int marginHeight = -1;
584     if (renderer->element()->hasTagName(frameTag) || renderer->element()->hasTagName(iframeTag)) {
585         HTMLFrameElement *o = static_cast<HTMLFrameElement *>(renderer->element());
586         allowsScrolling = o->scrollingMode() != ScrollBarAlwaysOff;
587         marginWidth = o->getMarginWidth();
588         marginHeight = o->getMarginHeight();
589     }
590
591     WebCoreFrameBridge *childBridge = [_bridge createChildFrameNamed:name
592                                                              withURL:url.getNSURL()
593                                                             referrer:referrer 
594                                                           renderPart:renderer
595                                                      allowsScrolling:allowsScrolling
596                                                          marginWidth:marginWidth
597                                                         marginHeight:marginHeight];
598     return [childBridge impl];
599
600     END_BLOCK_OBJC_EXCEPTIONS;
601     return 0;
602 }
603
604 void FrameMac::setView(FrameView *view)
605 {
606     // Detach the document now, so any onUnload handlers get run - if
607     // we wait until the view is destroyed, then things won't be
608     // hooked up enough for some JavaScript calls to work.
609     if (d->m_doc && view == 0)
610         d->m_doc->detach();
611     
612     d->m_view = view;
613     
614     // Delete old PlugIn data structures
615     cleanupPluginRootObjects();
616     _bindingRoot = 0;
617     KWQRelease(_windowScriptObject);
618     _windowScriptObject = 0;
619
620     if (_windowScriptNPObject) {
621         // Call _NPN_DeallocateObject() instead of _NPN_ReleaseObject() so that we don't leak if a plugin fails to release the window
622         // script object properly.
623         // This shouldn't cause any problems for plugins since they should have already been stopped and destroyed at this point.
624         _NPN_DeallocateObject(_windowScriptNPObject);
625         _windowScriptNPObject = 0;
626     }
627
628     // Only one form submission is allowed per view of a part.
629     // Since this part may be getting reused as a result of being
630     // pulled from the back/forward cache, reset this flag.
631     d->m_submittedFormURL = KURL();
632 }
633
634 void FrameMac::setTitle(const String &title)
635 {
636     String text = title;
637     text.replace('\\', backslashAsCurrencySymbol());
638
639     BEGIN_BLOCK_OBJC_EXCEPTIONS;
640     [_bridge setTitle:text];
641     END_BLOCK_OBJC_EXCEPTIONS;
642 }
643
644 void FrameMac::setStatusBarText(const String& status)
645 {
646     String text = status;
647     text.replace('\\', backslashAsCurrencySymbol());
648     
649     BEGIN_BLOCK_OBJC_EXCEPTIONS;
650     [_bridge setStatusText:text];
651     END_BLOCK_OBJC_EXCEPTIONS;
652 }
653
654 void FrameMac::scheduleClose()
655 {
656     if (!shouldClose())
657         return;
658     BEGIN_BLOCK_OBJC_EXCEPTIONS;
659     [_bridge closeWindowSoon];
660     END_BLOCK_OBJC_EXCEPTIONS;
661 }
662
663 void FrameMac::unfocusWindow()
664 {
665     BEGIN_BLOCK_OBJC_EXCEPTIONS;
666     [_bridge unfocusWindow];
667     END_BLOCK_OBJC_EXCEPTIONS;
668 }
669
670 String FrameMac::advanceToNextMisspelling(bool startBeforeSelection)
671 {
672     int exception = 0;
673
674     // The basic approach is to search in two phases - from the selection end to the end of the doc, and
675     // then we wrap and search from the doc start to (approximately) where we started.
676     
677     // Start at the end of the selection, search to edge of document.  Starting at the selection end makes
678     // repeated "check spelling" commands work.
679     RefPtr<Range> searchRange(rangeOfContents(document()));
680     bool startedWithSelection = false;
681     if (selection().start().node()) {
682         startedWithSelection = true;
683         if (startBeforeSelection) {
684             VisiblePosition start(selection().start(), selection().affinity());
685             // We match AppKit's rule: Start 1 character before the selection.
686             VisiblePosition oneBeforeStart = start.previous();
687             setStart(searchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start);
688         } else
689             setStart(searchRange.get(), VisiblePosition(selection().end(), selection().affinity()));
690     }
691
692     // If we're not in an editable node, try to find one, make that our range to work in
693     Node *editableNode = searchRange->startContainer(exception);
694     if (!editableNode->isContentEditable()) {
695         editableNode = editableNode->nextEditable();
696         if (!editableNode) {
697             return String();
698         }
699         searchRange->setStartBefore(editableNode, exception);
700         startedWithSelection = false;   // won't need to wrap
701     }
702     
703     // topNode defines the whole range we want to operate on 
704     Node *topNode = editableNode->rootEditableElement();
705     searchRange->setEndAfter(topNode, exception);
706
707     // Make sure start of searchRange is not in the middle of a word.  Jumping back a char and then
708     // forward by a word happens to do the trick.
709     if (startedWithSelection) {
710         VisiblePosition oneBeforeStart = startVisiblePosition(searchRange.get(), DOWNSTREAM).previous();
711         if (oneBeforeStart.isNotNull()) {
712             setStart(searchRange.get(), endOfWord(oneBeforeStart));
713         } // else we were already at the start of the editable node
714     }
715     
716     if (searchRange->collapsed(exception))
717         return String();       // nothing to search in
718     
719     // Get the spell checker if it is available
720     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
721     if (checker == nil)
722         return String();
723         
724     WordAwareIterator it(searchRange.get());
725     bool wrapped = false;
726     
727     // We go to the end of our first range instead of the start of it, just to be sure
728     // we don't get foiled by any word boundary problems at the start.  It means we might
729     // do a tiny bit more searching.
730     Node *searchEndAfterWrapNode = it.range()->endContainer(exception);
731     int searchEndAfterWrapOffset = it.range()->endOffset(exception);
732
733     while (1) {
734         if (!it.atEnd()) {      // we may be starting at the end of the doc, and already by atEnd
735             const UChar* chars = it.characters();
736             int len = it.length();
737             if (len > 1 || !QChar(chars[0]).isSpace()) {
738                 NSString *chunk = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(chars) length:len freeWhenDone:NO];
739                 NSRange misspelling = [checker checkSpellingOfString:chunk startingAt:0 language:nil wrap:NO inSpellDocumentWithTag:[_bridge spellCheckerDocumentTag] wordCount:NULL];
740                 [chunk release];
741                 if (misspelling.length > 0) {
742                     // Build up result range and string.  Note the misspelling may span many text nodes,
743                     // but the CharIterator insulates us from this complexity
744                     RefPtr<Range> misspellingRange(rangeOfContents(document()));
745                     CharacterIterator chars(it.range().get());
746                     chars.advance(misspelling.location);
747                     misspellingRange->setStart(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
748                     DeprecatedString result = chars.string(misspelling.length);
749                     misspellingRange->setEnd(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
750
751                     setSelection(SelectionController(misspellingRange.get(), DOWNSTREAM));
752                     revealSelection();
753                     // Mark misspelling in document.
754                     document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
755                     return result;
756                 }
757             }
758         
759             it.advance();
760         }
761         if (it.atEnd()) {
762             if (wrapped || !startedWithSelection) {
763                 break;      // finished the second range, or we did the whole doc with the first range
764             } else {
765                 // we've gone from the selection to the end of doc, now wrap around
766                 wrapped = YES;
767                 searchRange->setStartBefore(topNode, exception);
768                 // going until the end of the very first chunk we tested is far enough
769                 searchRange->setEnd(searchEndAfterWrapNode, searchEndAfterWrapOffset, exception);
770                 it = WordAwareIterator(searchRange.get());
771             }
772         }   
773     }
774     
775     return String();
776 }
777
778 bool FrameMac::wheelEvent(NSEvent *event)
779 {
780     FrameView *v = d->m_view.get();
781
782     if (v) {
783         NSEvent *oldCurrentEvent = _currentEvent;
784         _currentEvent = KWQRetain(event);
785
786         PlatformWheelEvent qEvent(event);
787         v->handleWheelEvent(qEvent);
788
789         ASSERT(_currentEvent == event);
790         KWQRelease(event);
791         _currentEvent = oldCurrentEvent;
792
793         if (qEvent.isAccepted())
794             return true;
795     }
796
797     // FIXME: The scrolling done here should be done in the default handlers
798     // of the elements rather than here in the part.
799
800     KWQScrollDirection direction;
801     float multiplier;
802     float deltaX = [event deltaX];
803     float deltaY = [event deltaY];
804     if (deltaX < 0) {
805         direction = KWQScrollRight;
806         multiplier = -deltaX;
807     } else if (deltaX > 0) {
808         direction = KWQScrollLeft;
809         multiplier = deltaX;
810     } else if (deltaY < 0) {
811         direction = KWQScrollDown;
812         multiplier = -deltaY;
813     }  else if (deltaY > 0) {
814         direction = KWQScrollUp;
815         multiplier = deltaY;
816     } else
817         return false;
818
819     RenderObject *r = renderer();
820     if (!r)
821         return false;
822     
823     NSPoint point = [d->m_view->getDocumentView() convertPoint:[event locationInWindow] fromView:nil];
824     RenderObject::NodeInfo nodeInfo(true, true);
825     r->layer()->hitTest(nodeInfo, IntPoint(point));    
826     
827     Node *node = nodeInfo.innerNode();
828     if (!node)
829         return false;
830     
831     r = node->renderer();
832     if (!r)
833         return false;
834     
835     return r->scroll(direction, KWQScrollWheel, multiplier);
836 }
837
838 void FrameMac::startRedirectionTimer()
839 {
840     stopRedirectionTimer();
841
842     Frame::startRedirectionTimer();
843
844     // Don't report history navigations, just actual redirection.
845     if (d->m_scheduledRedirection != historyNavigationScheduled) {
846         NSTimeInterval interval = d->m_redirectionTimer.nextFireInterval();
847         NSDate *fireDate = [[NSDate alloc] initWithTimeIntervalSinceNow:interval];
848         [_bridge reportClientRedirectToURL:KURL(d->m_redirectURL).getNSURL()
849                                      delay:d->m_delayRedirect
850                                   fireDate:fireDate
851                                lockHistory:d->m_redirectLockHistory
852                                isJavaScriptFormAction:d->m_executingJavaScriptFormAction];
853         [fireDate release];
854     }
855 }
856
857 void FrameMac::stopRedirectionTimer()
858 {
859     bool wasActive = d->m_redirectionTimer.isActive();
860
861     Frame::stopRedirectionTimer();
862
863     // Don't report history navigations, just actual redirection.
864     if (wasActive && d->m_scheduledRedirection != historyNavigationScheduled)
865         [_bridge reportClientRedirectCancelled:d->m_cancelWithLoadInProgress];
866 }
867
868 String FrameMac::userAgent() const
869 {
870     BEGIN_BLOCK_OBJC_EXCEPTIONS;
871     return [_bridge userAgentForURL:url().getNSURL()];
872     END_BLOCK_OBJC_EXCEPTIONS;
873          
874     return String();
875 }
876
877 String FrameMac::mimeTypeForFileName(const String& fileName) const
878 {
879     BEGIN_BLOCK_OBJC_EXCEPTIONS;
880     return [_bridge MIMETypeForPath:fileName];
881     END_BLOCK_OBJC_EXCEPTIONS;
882
883     return String();
884 }
885
886 NSView* FrameMac::nextKeyViewInFrame(Node* node, KWQSelectionDirection direction)
887 {
888     Document* doc = document();
889     if (!doc)
890         return nil;
891     
892     for (;;) {
893         node = direction == KWQSelectingNext
894             ? doc->nextFocusNode(node) : doc->previousFocusNode(node);
895         if (!node)
896             return nil;
897         
898         RenderObject* renderer = node->renderer();
899         if (!renderer->isWidget()) {
900             static_cast<Element*>(node)->focus(); 
901             [_bridge willMakeFirstResponderForNodeFocus];
902             return [_bridge documentView];
903         }
904
905         if (Widget* widget = static_cast<RenderWidget*>(renderer)->widget()) {
906             NSView* view;
907             if (widget->isFrameView())
908                 view = Mac(static_cast<FrameView*>(widget)->frame())->nextKeyViewInFrame(0, direction);
909             else
910                 view = widget->getView();
911             if (view)
912                 return view;
913         }
914     }
915 }
916
917 NSView *FrameMac::nextKeyViewInFrameHierarchy(Node *node, KWQSelectionDirection direction)
918 {
919     NSView *next = nextKeyViewInFrame(node, direction);
920     if (!next)
921         if (FrameMac *parent = Mac(tree()->parent()))
922             next = parent->nextKeyViewInFrameHierarchy(ownerElement(), direction);
923     
924     // remove focus from currently focused node if we're giving focus to another view
925     if (next && (next != [_bridge documentView]))
926         if (Document *doc = document())
927             doc->setFocusNode(0);
928     
929     return next;
930 }
931
932 NSView *FrameMac::nextKeyView(Node *node, KWQSelectionDirection direction)
933 {
934     NSView * next;
935     BEGIN_BLOCK_OBJC_EXCEPTIONS;
936
937     next = nextKeyViewInFrameHierarchy(node, direction);
938     if (next)
939         return next;
940
941     // Look at views from the top level part up, looking for a next key view that we can use.
942
943     next = direction == KWQSelectingNext
944         ? [_bridge nextKeyViewOutsideWebFrameViews]
945         : [_bridge previousKeyViewOutsideWebFrameViews];
946
947     if (next)
948         return next;
949
950     END_BLOCK_OBJC_EXCEPTIONS;
951     
952     // If all else fails, make a loop by starting from 0.
953     return nextKeyViewInFrameHierarchy(0, direction);
954 }
955
956 NSView *FrameMac::nextKeyViewForWidget(Widget *startingWidget, KWQSelectionDirection direction)
957 {
958     // Use the event filter object to figure out which RenderWidget owns this Widget and get to the DOM.
959     // Then get the next key view in the order determined by the DOM.
960     Node *node = nodeForWidget(startingWidget);
961     ASSERT(node);
962     return Mac(frameForNode(node))->nextKeyView(node, direction);
963 }
964
965 bool FrameMac::currentEventIsMouseDownInWidget(Widget *candidate)
966 {
967     BEGIN_BLOCK_OBJC_EXCEPTIONS;
968     switch ([[NSApp currentEvent] type]) {
969         case NSLeftMouseDown:
970         case NSRightMouseDown:
971         case NSOtherMouseDown:
972             break;
973         default:
974             return NO;
975     }
976     END_BLOCK_OBJC_EXCEPTIONS;
977     
978     Node *node = nodeForWidget(candidate);
979     ASSERT(node);
980     return frameForNode(node)->d->m_view->nodeUnderMouse() == node;
981 }
982
983 bool FrameMac::currentEventIsKeyboardOptionTab()
984 {
985     BEGIN_BLOCK_OBJC_EXCEPTIONS;
986     NSEvent *evt = [NSApp currentEvent];
987     if ([evt type] != NSKeyDown) {
988         return NO;
989     }
990
991     if (([evt modifierFlags] & NSAlternateKeyMask) == 0) {
992         return NO;
993     }
994     
995     NSString *chars = [evt charactersIgnoringModifiers];
996     if ([chars length] != 1)
997         return NO;
998     
999     const unichar tabKey = 0x0009;
1000     const unichar shiftTabKey = 0x0019;
1001     unichar c = [chars characterAtIndex:0];
1002     if (c != tabKey && c != shiftTabKey)
1003         return NO;
1004     
1005     END_BLOCK_OBJC_EXCEPTIONS;
1006     return YES;
1007 }
1008
1009 bool FrameMac::handleKeyboardOptionTabInView(NSView *view)
1010 {
1011     if (FrameMac::currentEventIsKeyboardOptionTab()) {
1012         if (([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) != 0) {
1013             [[view window] selectKeyViewPrecedingView:view];
1014         } else {
1015             [[view window] selectKeyViewFollowingView:view];
1016         }
1017         return YES;
1018     }
1019     
1020     return NO;
1021 }
1022
1023 bool FrameMac::tabsToLinks() const
1024 {
1025     if ([_bridge keyboardUIMode] & WebCoreKeyboardAccessTabsToLinks)
1026         return !FrameMac::currentEventIsKeyboardOptionTab();
1027     else
1028         return FrameMac::currentEventIsKeyboardOptionTab();
1029 }
1030
1031 bool FrameMac::tabsToAllControls() const
1032 {
1033     WebCoreKeyboardUIMode keyboardUIMode = [_bridge keyboardUIMode];
1034     BOOL handlingOptionTab = FrameMac::currentEventIsKeyboardOptionTab();
1035
1036     // If tab-to-links is off, option-tab always highlights all controls
1037     if ((keyboardUIMode & WebCoreKeyboardAccessTabsToLinks) == 0 && handlingOptionTab) {
1038         return YES;
1039     }
1040     
1041     // If system preferences say to include all controls, we always include all controls
1042     if (keyboardUIMode & WebCoreKeyboardAccessFull) {
1043         return YES;
1044     }
1045     
1046     // Otherwise tab-to-links includes all controls, unless the sense is flipped via option-tab.
1047     if (keyboardUIMode & WebCoreKeyboardAccessTabsToLinks) {
1048         return !handlingOptionTab;
1049     }
1050     
1051     return handlingOptionTab;
1052 }
1053
1054 KJS::Bindings::RootObject *FrameMac::executionContextForDOM()
1055 {
1056     return bindingRootObject();
1057 }
1058
1059 KJS::Bindings::RootObject *FrameMac::bindingRootObject()
1060 {
1061     assert(jScriptEnabled());
1062     if (!_bindingRoot) {
1063         JSLock lock;
1064         _bindingRoot = new KJS::Bindings::RootObject(0);    // The root gets deleted by JavaScriptCore.
1065         KJS::JSObject *win = KJS::Window::retrieveWindow(this);
1066         _bindingRoot->setRootObjectImp (win);
1067         _bindingRoot->setInterpreter(jScript()->interpreter());
1068         addPluginRootObject (_bindingRoot);
1069     }
1070     return _bindingRoot;
1071 }
1072
1073 WebScriptObject *FrameMac::windowScriptObject()
1074 {
1075     if (!jScriptEnabled())
1076         return 0;
1077
1078     if (!_windowScriptObject) {
1079         KJS::JSLock lock;
1080         KJS::JSObject *win = KJS::Window::retrieveWindow(this);
1081         _windowScriptObject = KWQRetainNSRelease([[WebScriptObject alloc] _initWithJSObject:win originExecutionContext:bindingRootObject() executionContext:bindingRootObject()]);
1082     }
1083
1084     return _windowScriptObject;
1085 }
1086
1087 NPObject *FrameMac::windowScriptNPObject()
1088 {
1089     if (!_windowScriptNPObject) {
1090         if (jScriptEnabled()) {
1091             // JavaScript is enabled, so there is a JavaScript window object.  Return an NPObject bound to the window
1092             // object.
1093             KJS::JSObject *win = KJS::Window::retrieveWindow(this);
1094             assert(win);
1095             _windowScriptNPObject = _NPN_CreateScriptObject(0, win, bindingRootObject(), bindingRootObject());
1096         } else {
1097             // JavaScript is not enabled, so we cannot bind the NPObject to the JavaScript window object.
1098             // Instead, we create an NPObject of a different class, one which is not bound to a JavaScript object.
1099             _windowScriptNPObject = _NPN_CreateNoScriptObject();
1100         }
1101     }
1102
1103     return _windowScriptNPObject;
1104 }
1105
1106 void FrameMac::partClearedInBegin()
1107 {
1108     if (jScriptEnabled())
1109         [_bridge windowObjectCleared];
1110 }
1111
1112 void FrameMac::openURLFromPageCache(KWQPageState *state)
1113 {
1114     // It's safe to assume none of the KWQPageState methods will raise
1115     // exceptions, since KWQPageState is implemented by WebCore and
1116     // does not throw
1117
1118     Document *doc = [state document];
1119     Node *mousePressNode = [state mousePressNode];
1120     KURL *kurl = [state URL];
1121     SavedProperties *windowProperties = [state windowProperties];
1122     SavedProperties *locationProperties = [state locationProperties];
1123     SavedBuiltins *interpreterBuiltins = [state interpreterBuiltins];
1124     PausedTimeouts *timeouts = [state pausedTimeouts];
1125     
1126     cancelRedirection();
1127
1128     // We still have to close the previous part page.
1129     closeURL();
1130             
1131     d->m_bComplete = false;
1132     
1133     // Don't re-emit the load event.
1134     d->m_bLoadEventEmitted = true;
1135     
1136     // delete old status bar msg's from kjs (if it _was_ activated on last URL)
1137     if (jScriptEnabled()) {
1138         d->m_kjsStatusBarText = String();
1139         d->m_kjsDefaultStatusBarText = String();
1140     }
1141
1142     ASSERT(kurl);
1143     
1144     d->m_url = *kurl;
1145     
1146     // 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
1147     // data arrives) (Simon)
1148     if (url().protocol().startsWith("http") && !url().host().isEmpty() && url().path().isEmpty())
1149         d->m_url.setPath("/");
1150     
1151     // copy to m_workingURL after fixing url() above
1152     d->m_workingURL = url();
1153         
1154     started();
1155     
1156     // -----------begin-----------
1157     clear();
1158
1159     doc->setInPageCache(NO);
1160
1161     d->m_bCleared = false;
1162     d->m_bComplete = false;
1163     d->m_bLoadEventEmitted = false;
1164     d->m_referrer = url().url();
1165     
1166     setView(doc->view());
1167     
1168     d->m_doc = doc;
1169     d->m_mousePressNode = mousePressNode;
1170     d->m_decoder = doc->decoder();
1171
1172     updatePolicyBaseURL();
1173
1174     { // scope the lock
1175         JSLock lock;
1176         restoreWindowProperties(windowProperties);
1177         restoreLocationProperties(locationProperties);
1178         restoreInterpreterBuiltins(*interpreterBuiltins);
1179     }
1180
1181     resumeTimeouts(timeouts);
1182     
1183     checkCompleted();
1184 }
1185
1186 WebCoreFrameBridge *FrameMac::bridgeForWidget(const Widget *widget)
1187 {
1188     ASSERT_ARG(widget, widget);
1189     
1190     FrameMac *frame = Mac(frameForWidget(widget));
1191     ASSERT(frame);
1192     return frame->bridge();
1193 }
1194
1195 NSView *FrameMac::documentViewForNode(Node *node)
1196 {
1197     WebCoreFrameBridge *bridge = Mac(frameForNode(node))->bridge();
1198     return [bridge documentView];
1199 }
1200
1201 void FrameMac::saveDocumentState()
1202 {
1203     // Do not save doc state if the page has a password field and a form that would be submitted
1204     // via https
1205     if (!(d->m_doc && d->m_doc->hasPasswordField() && d->m_doc->hasSecureForm())) {
1206         BEGIN_BLOCK_OBJC_EXCEPTIONS;
1207         [_bridge saveDocumentState];
1208         END_BLOCK_OBJC_EXCEPTIONS;
1209     }
1210 }
1211
1212 void FrameMac::restoreDocumentState()
1213 {
1214     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1215     [_bridge restoreDocumentState];
1216     END_BLOCK_OBJC_EXCEPTIONS;
1217 }
1218
1219 String FrameMac::incomingReferrer() const
1220 {
1221     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1222     return [_bridge incomingReferrer];
1223     END_BLOCK_OBJC_EXCEPTIONS;
1224
1225     return String();
1226 }
1227
1228 void FrameMac::runJavaScriptAlert(const String& message)
1229 {
1230     String text = message;
1231     text.replace('\\', backslashAsCurrencySymbol());
1232     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1233     [_bridge runJavaScriptAlertPanelWithMessage:text];
1234     END_BLOCK_OBJC_EXCEPTIONS;
1235 }
1236
1237 bool FrameMac::runJavaScriptConfirm(const String& message)
1238 {
1239     String text = message;
1240     text.replace('\\', backslashAsCurrencySymbol());
1241
1242     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1243     return [_bridge runJavaScriptConfirmPanelWithMessage:text];
1244     END_BLOCK_OBJC_EXCEPTIONS;
1245
1246     return false;
1247 }
1248
1249 bool FrameMac::runJavaScriptPrompt(const String& prompt, const String& defaultValue, String& result)
1250 {
1251     String promptText = prompt;
1252     promptText.replace('\\', backslashAsCurrencySymbol());
1253     String defaultValueText = defaultValue;
1254     defaultValueText.replace('\\', backslashAsCurrencySymbol());
1255
1256     bool ok;
1257     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1258     NSString *returnedText = nil;
1259
1260     ok = [_bridge runJavaScriptTextInputPanelWithPrompt:prompt
1261         defaultText:defaultValue returningText:&returnedText];
1262
1263     if (ok) {
1264         result = String(returnedText);
1265         result.replace(backslashAsCurrencySymbol(), '\\');
1266     }
1267
1268     return ok;
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 void FrameMac::setDisplaysWithFocusAttributes(bool flag)
2870 {
2871     if (d->m_isFocused == flag)
2872         return;
2873     
2874     Frame::setDisplaysWithFocusAttributes(flag);
2875     Document *doc = document();
2876     // Mac Specific: Changing the tint of controls from clear to aqua/graphite and vice versa.  We
2877     // do a "fake" paint.  When the theme gets a paint call, it can then do an invalidate.
2878     if (doc && d->m_view && d->m_view->getDocumentView() && theme()->supportsControlTints() && renderer()) {
2879         doc->updateLayout(); // Ensure layout is up to date.
2880         IntRect visibleRect(enclosingIntRect(d->m_view->visibleContentRect()));
2881         GraphicsContext context(0);
2882         context.setUpdatingControlTints(true);
2883         paint(&context, visibleRect);
2884     }
2885 }
2886
2887 NSColor *FrameMac::bodyBackgroundColor() const
2888 {
2889     if (document() && document()->body() && document()->body()->renderer()) {
2890         Color bgColor = document()->body()->renderer()->style()->backgroundColor();
2891         if (bgColor.isValid())
2892             return nsColor(bgColor);
2893     }
2894     return nil;
2895 }
2896
2897 WebCoreKeyboardUIMode FrameMac::keyboardUIMode() const
2898 {
2899     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2900     return [_bridge keyboardUIMode];
2901     END_BLOCK_OBJC_EXCEPTIONS;
2902
2903     return WebCoreKeyboardAccessDefault;
2904 }
2905
2906 void FrameMac::didTellBridgeAboutLoad(const String& URL)
2907 {
2908     urlsBridgeKnowsAbout.add(URL.impl());
2909 }
2910
2911 bool FrameMac::haveToldBridgeAboutLoad(const String& URL)
2912 {
2913     return urlsBridgeKnowsAbout.contains(URL.impl());
2914 }
2915
2916 void FrameMac::clear()
2917 {
2918     urlsBridgeKnowsAbout.clear();
2919     setMarkedTextRange(0, nil, nil);
2920     Frame::clear();
2921 }
2922
2923 void FrameMac::print()
2924 {
2925     [Mac(this)->_bridge print];
2926 }
2927
2928 KJS::Bindings::Instance *FrameMac::getAppletInstanceForWidget(Widget *widget)
2929 {
2930     NSView *aView = widget->getView();
2931     if (!aView)
2932         return 0;
2933     jobject applet;
2934     
2935     // Get a pointer to the actual Java applet instance.
2936     if ([_bridge respondsToSelector:@selector(getAppletInView:)])
2937         applet = [_bridge getAppletInView:aView];
2938     else
2939         applet = [_bridge pollForAppletInView:aView];
2940     
2941     if (applet) {
2942         // Wrap the Java instance in a language neutral binding and hand
2943         // off ownership to the APPLET element.
2944         KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2945         KJS::Bindings::Instance *instance = KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::JavaLanguage, applet, executionContext);        
2946         return instance;
2947     }
2948     
2949     return 0;
2950 }
2951
2952 static KJS::Bindings::Instance *getInstanceForView(NSView *aView)
2953 {
2954     if ([aView respondsToSelector:@selector(objectForWebScript)]){
2955         id object = [aView objectForWebScript];
2956         if (object) {
2957             KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2958             return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::ObjectiveCLanguage, object, executionContext);
2959         }
2960     }
2961     else if ([aView respondsToSelector:@selector(pluginScriptableObject)]){
2962         void *object = [aView pluginScriptableObject];
2963         if (object) {
2964             KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2965             return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::CLanguage, object, executionContext);
2966         }
2967     }
2968     return 0;
2969 }
2970
2971 KJS::Bindings::Instance *FrameMac::getEmbedInstanceForWidget(Widget *widget)
2972 {
2973     return getInstanceForView(widget->getView());
2974 }
2975
2976 KJS::Bindings::Instance *FrameMac::getObjectInstanceForWidget(Widget *widget)
2977 {
2978     return getInstanceForView(widget->getView());
2979 }
2980
2981 void FrameMac::addPluginRootObject(KJS::Bindings::RootObject *root)
2982 {
2983     m_rootObjects.append(root);
2984 }
2985
2986 void FrameMac::cleanupPluginRootObjects()
2987 {
2988     JSLock lock;
2989
2990     unsigned count = m_rootObjects.size();
2991     for (unsigned i = 0; i < count; i++)
2992         m_rootObjects[i]->removeAllNativeReferences();
2993     m_rootObjects.clear();
2994 }
2995
2996 void FrameMac::registerCommandForUndoOrRedo(const EditCommandPtr &cmd, bool isRedo)
2997 {
2998     ASSERT(cmd.get());
2999     KWQEditCommand *kwq = [KWQEditCommand commandWithEditCommand:cmd.get()];
3000     NSUndoManager *undoManager = [_bridge undoManager];
3001     [undoManager registerUndoWithTarget:_bridge selector:(isRedo ? @selector(redoEditing:) : @selector(undoEditing:)) object:kwq];
3002     NSString *actionName = [_bridge nameForUndoAction:static_cast<WebUndoAction>(cmd.editingAction())];
3003     if (actionName)
3004         [undoManager setActionName:actionName];
3005     _haveUndoRedoOperations = YES;
3006 }
3007
3008 void FrameMac::registerCommandForUndo(const EditCommandPtr &cmd)
3009 {
3010     registerCommandForUndoOrRedo(cmd, NO);
3011 }
3012
3013 void FrameMac::registerCommandForRedo(const EditCommandPtr &cmd)
3014 {
3015     registerCommandForUndoOrRedo(cmd, YES);
3016 }
3017
3018 void FrameMac::clearUndoRedoOperations()
3019 {
3020     if (_haveUndoRedoOperations) {
3021         [[_bridge undoManager] removeAllActionsWithTarget:_bridge];
3022         _haveUndoRedoOperations = NO;
3023     }
3024 }
3025
3026 void FrameMac::issueUndoCommand()
3027 {
3028     if (canUndo())
3029         [[_bridge undoManager] undo];
3030 }
3031
3032 void FrameMac::issueRedoCommand()
3033 {
3034     if (canRedo())
3035         [[_bridge undoManager] redo];
3036 }
3037
3038 void FrameMac::issueCutCommand()
3039 {
3040     [_bridge issueCutCommand];
3041 }
3042
3043 void FrameMac::issueCopyCommand()
3044 {
3045     [_bridge issueCopyCommand];
3046 }
3047
3048 void FrameMac::issuePasteCommand()
3049 {
3050     [_bridge issuePasteCommand];
3051 }
3052
3053 void FrameMac::issuePasteAndMatchStyleCommand()
3054 {
3055     [_bridge issuePasteAndMatchStyleCommand];
3056 }
3057
3058 void FrameMac::issueTransposeCommand()
3059 {
3060     [_bridge issueTransposeCommand];
3061 }
3062
3063 bool FrameMac::canUndo() const
3064 {
3065     return [[Mac(this)->_bridge undoManager] canUndo];
3066 }
3067
3068 bool FrameMac::canRedo() const
3069 {
3070     return [[Mac(this)->_bridge undoManager] canRedo];
3071 }
3072
3073 bool FrameMac::canPaste() const
3074 {
3075     return [Mac(this)->_bridge canPaste];
3076 }
3077
3078 void FrameMac::markMisspellingsInAdjacentWords(const VisiblePosition &p)
3079 {
3080     if (![_bridge isContinuousSpellCheckingEnabled])
3081         return;
3082     markMisspellings(SelectionController(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)));
3083 }
3084
3085 void FrameMac::markMisspellings(const SelectionController &selection)
3086 {
3087     // This function is called with a selection already expanded to word boundaries.
3088     // Might be nice to assert that here.
3089
3090     if (![_bridge isContinuousSpellCheckingEnabled])
3091         return;
3092
3093     RefPtr<Range> searchRange(selection.toRange());
3094     if (!searchRange || searchRange->isDetached())
3095         return;
3096     
3097     // If we're not in an editable node, bail.
3098     int exception = 0;
3099     Node *editableNode = searchRange->startContainer(exception);
3100     if (!editableNode->isContentEditable())
3101         return;
3102     
3103     // Get the spell checker if it is available
3104     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
3105     if (checker == nil)
3106         return;
3107     
3108     WordAwareIterator it(searchRange.get());
3109     
3110     while (!it.atEnd()) {      // we may be starting at the end of the doc, and already by atEnd
3111         const UChar* chars = it.characters();
3112         int len = it.length();
3113         if (len > 1 || !QChar(chars[0]).isSpace()) {
3114             NSString *chunk = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(chars) length:len freeWhenDone:NO];
3115             int startIndex = 0;
3116             // Loop over the chunk to find each misspelling in it.
3117             while (startIndex < len) {
3118                 NSRange misspelling = [checker checkSpellingOfString:chunk startingAt:startIndex language:nil wrap:NO inSpellDocumentWithTag:[_bridge spellCheckerDocumentTag] wordCount:NULL];
3119                 if (misspelling.length == 0)
3120                     break;
3121                 else {
3122                     // Build up result range and string.  Note the misspelling may span many text nodes,
3123                     // but the CharIterator insulates us from this complexity
3124                     RefPtr<Range> misspellingRange(rangeOfContents(document()));
3125                     CharacterIterator chars(it.range().get());
3126                     chars.advance(misspelling.location);
3127                     misspellingRange->setStart(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
3128                     chars.advance(misspelling.length);
3129                     misspellingRange->setEnd(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
3130                     // Mark misspelling in document.
3131                     document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
3132                     startIndex = misspelling.location + misspelling.length;
3133                 }
3134             }
3135             [chunk release];
3136         }
3137     
3138         it.advance();
3139     }
3140 }
3141
3142 void FrameMac::respondToChangedSelection(const SelectionController &oldSelection, bool closeTyping)
3143 {
3144     if (document()) {
3145         if ([_bridge isContinuousSpellCheckingEnabled]) {
3146             SelectionController oldAdjacentWords = SelectionController();
3147             
3148             // If this is a change in selection resulting from a delete operation, oldSelection may no longer
3149             // be in the document.
3150             if (oldSelection.start().node() && oldSelection.start().node()->inDocument()) {
3151                 VisiblePosition oldStart(oldSelection.start(), oldSelection.affinity());
3152                 oldAdjacentWords = SelectionController(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));   
3153             }
3154
3155             VisiblePosition newStart(selection().start(), selection().affinity());
3156             SelectionController newAdjacentWords(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
3157
3158             if (oldAdjacentWords != newAdjacentWords) {
3159                 // Mark misspellings in the portion that was previously unmarked because of
3160                 // the proximity of the start of the selection. We only spell check words in
3161                 // the vicinity of the start of the old selection because the spelling checker
3162                 // is not fast enough to do a lot of spelling checking implicitly. This matches
3163                 // AppKit. This function is really the only code that knows that rule. The
3164                 // markMisspellings function is prepared to handler larger ranges.
3165
3166                 // When typing we check spelling elsewhere, so don't redo it here.
3167                 if (closeTyping)
3168                     markMisspellings(oldAdjacentWords);
3169
3170                 // This only erases a marker in the first word of the selection.
3171                 // Perhaps peculiar, but it matches AppKit.
3172                 document()->removeMarkers(newAdjacentWords.toRange().get(), DocumentMarker::Spelling);
3173             }
3174         } else
3175             // When continuous spell checking is off, no markers appear after the selection changes.
3176             document()->removeMarkers(DocumentMarker::Spelling);
3177     }
3178
3179     [_bridge respondToChangedSelection];
3180 }
3181
3182 bool FrameMac::shouldChangeSelection(const SelectionController &oldSelection, const SelectionController &newSelection, EAffinity affinity, bool stillSelecting) const
3183 {
3184     return [_bridge shouldChangeSelectedDOMRange:[DOMRange _rangeWith:oldSelection.toRange().get()]
3185                                       toDOMRange:[DOMRange _rangeWith:newSelection.toRange().get()]
3186                                         affinity:static_cast<NSSelectionAffinity>(affinity)
3187                                   stillSelecting:stillSelecting];
3188 }
3189
3190 void FrameMac::respondToChangedContents()
3191 {
3192     if (AccessibilityObjectCache::accessibilityEnabled())
3193         renderer()->document()->getAccObjectCache()->postNotificationToTopWebArea(renderer(), "AXValueChanged");
3194     [_bridge respondToChangedContents];
3195 }
3196
3197 bool FrameMac::isContentEditable() const
3198 {
3199     return Frame::isContentEditable() || [_bridge isEditable];
3200 }
3201
3202 bool FrameMac::shouldBeginEditing(const Range *range) const
3203 {
3204     ASSERT(range);
3205     return [_bridge shouldBeginEditing:[DOMRange _rangeWith:const_cast<Range *>(range)]];
3206 }
3207
3208 bool FrameMac::shouldEndEditing(const Range *range) const
3209 {
3210     ASSERT(range);
3211     return [_bridge shouldEndEditing:[DOMRange _rangeWith:const_cast<Range *>(range)]];
3212 }
3213
3214 void FrameMac::didBeginEditing() const
3215 {
3216     [_bridge didBeginEditing];
3217 }
3218
3219 void FrameMac::didEndEditing() const
3220 {
3221     [_bridge didEndEditing];
3222 }
3223
3224 void FrameMac::textFieldDidBeginEditing(Element* input)
3225 {
3226     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3227     [_bridge textFieldDidBeginEditing:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
3228     END_BLOCK_OBJC_EXCEPTIONS;
3229 }
3230
3231 void FrameMac::textFieldDidEndEditing(Element* input)
3232 {
3233     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3234     [_bridge textFieldDidEndEditing:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
3235     END_BLOCK_OBJC_EXCEPTIONS;
3236 }
3237
3238 void FrameMac::textDidChangeInTextField(Element* input)
3239 {
3240     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3241     [_bridge textDidChangeInTextField:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
3242     END_BLOCK_OBJC_EXCEPTIONS;
3243 }
3244
3245 bool FrameMac::doTextFieldCommandFromEvent(Element* input, const PlatformKeyboardEvent* event)
3246 {
3247     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3248     return [_bridge textField:(DOMHTMLInputElement *)[DOMElement _elementWith:input] doCommandBySelector:selectorForKeyEvent(event)];
3249     END_BLOCK_OBJC_EXCEPTIONS;
3250     return false;
3251 }
3252
3253 void FrameMac::textWillBeDeletedInTextField(Element* input)
3254 {
3255     // We're using the deleteBackward selector for all deletion operations since the autofill code treats all deletions the same way.
3256     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3257     [_bridge textField:(DOMHTMLInputElement *)[DOMElement _elementWith:input] doCommandBySelector:@selector(deleteBackward:)];
3258     END_BLOCK_OBJC_EXCEPTIONS;
3259 }
3260
3261 static DeprecatedValueList<MarkedTextUnderline> convertAttributesToUnderlines(const Range *markedTextRange, NSArray *attributes, NSArray *ranges)
3262 {
3263     DeprecatedValueList<MarkedTextUnderline> result;
3264
3265     int exception = 0;
3266     int baseOffset = markedTextRange->startOffset(exception);
3267
3268     unsigned length = [attributes count];
3269     ASSERT([ranges count] == length);
3270
3271     for (unsigned i = 0; i < length; i++) {
3272         NSNumber *style = [[attributes objectAtIndex:i] objectForKey:NSUnderlineStyleAttributeName];
3273         if (!style)
3274             continue;
3275         NSRange range = [[ranges objectAtIndex:i] rangeValue];
3276         NSColor *color = [[attributes objectAtIndex:i] objectForKey:NSUnderlineColorAttributeName];
3277         Color qColor = Color::black;
3278         if (color) {
3279             NSColor* deviceColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
3280             qColor = Color(makeRGBA((int)(255 * [deviceColor redComponent]),
3281                                     (int)(255 * [deviceColor blueComponent]),
3282                                     (int)(255 * [deviceColor greenComponent]),
3283                                     (int)(255 * [deviceColor alphaComponent])));
3284         }
3285
3286         result.append(MarkedTextUnderline(range.location + baseOffset, 
3287                                           range.location + baseOffset + range.length, 
3288                                           qColor,
3289                                           [style intValue] > 1));
3290     }
3291
3292     return result;
3293 }
3294
3295 void FrameMac::setMarkedTextRange(const Range *range, NSArray *attributes, NSArray *ranges)
3296 {
3297     int exception = 0;
3298
3299     ASSERT(!range || range->startContainer(exception) == range->endContainer(exception));
3300     ASSERT(!range || range->collapsed(exception) || range->startContainer(exception)->isTextNode());
3301
3302     if (attributes == nil) {
3303         d->m_markedTextUsesUnderlines = false;
3304         d->m_markedTextUnderlines.clear();
3305     } else {
3306         d->m_markedTextUsesUnderlines = true;
3307         d->m_markedTextUnderlines = convertAttributesToUnderlines(range, attributes, ranges);
3308     }
3309
3310     if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
3311         m_markedTextRange->startContainer(exception)->renderer()->repaint();
3312
3313     if (range && range->collapsed(exception))
3314         m_markedTextRange = 0;
3315     else
3316         m_markedTextRange = const_cast<Range *>(range);
3317
3318     if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
3319         m_markedTextRange->startContainer(exception)->renderer()->repaint();
3320 }
3321
3322 bool FrameMac::canGoBackOrForward(int distance) const
3323 {
3324     return [_bridge canGoBackOrForward:distance];
3325 }
3326
3327 void FrameMac::didFirstLayout()
3328 {
3329     [_bridge didFirstLayout];
3330 }
3331
3332 NSMutableDictionary *FrameMac::dashboardRegionsDictionary()
3333 {
3334     Document *doc = document();
3335     if (!doc)
3336         return nil;
3337
3338     const DeprecatedValueList<DashboardRegionValue> regions = doc->dashboardRegions();
3339     unsigned i, count = regions.count();
3340
3341     // Convert the DeprecatedValueList<DashboardRegionValue> into a NSDictionary of WebDashboardRegions
3342     NSMutableDictionary *webRegions = [[[NSMutableDictionary alloc] initWithCapacity:count] autorelease];
3343     for (i = 0; i < count; i++) {
3344         DashboardRegionValue region = regions[i];
3345
3346         if (region.type == StyleDashboardRegion::None)
3347             continue;
3348         
3349         NSString *label = region.label;
3350         WebDashboardRegionType type = WebDashboardRegionTypeNone;
3351         if (region.type == StyleDashboardRegion::Circle)
3352             type = WebDashboardRegionTypeCircle;
3353         else if (region.type == StyleDashboardRegion::Rectangle)
3354             type = WebDashboardRegionTypeRectangle;
3355         NSMutableArray *regionValues = [webRegions objectForKey:label];
3356         if (!regionValues) {
3357             regionValues = [NSMutableArray array];
3358             [webRegions setObject:regionValues forKey:label];
3359         }
3360         
3361         WebDashboardRegion *webRegion = [[[WebDashboardRegion alloc] initWithRect:region.bounds clip:region.clip type:type] autorelease];
3362         [regionValues addObject:webRegion];
3363     }
3364     
3365     return webRegions;
3366 }
3367
3368 void FrameMac::dashboardRegionsChanged()
3369 {
3370     NSMutableDictionary *webRegions = dashboardRegionsDictionary();
3371     [_bridge dashboardRegionsChanged:webRegions];
3372 }
3373
3374 bool FrameMac::isCharacterSmartReplaceExempt(const QChar &c, bool isPreviousChar)
3375 {
3376     return [_bridge isCharacterSmartReplaceExempt:c.unicode() isPreviousCharacter:isPreviousChar];
3377 }
3378
3379 void FrameMac::handledOnloadEvents()
3380 {
3381     [_bridge handledOnloadEvents];
3382 }
3383
3384 bool FrameMac::shouldClose()
3385 {
3386     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3387
3388     if (![_bridge canRunBeforeUnloadConfirmPanel])
3389         return true;
3390
3391     RefPtr<Document> doc = document();
3392     if (!doc)
3393         return true;
3394     HTMLElement* body = doc->body();
3395     if (!body)
3396         return true;
3397
3398     RefPtr<BeforeUnloadEvent> event = new BeforeUnloadEvent;
3399     event->setTarget(doc.get());
3400     doc->handleWindowEvent(event.get(), false);
3401
3402     if (!event->defaultPrevented() && doc)
3403         doc->defaultEventHandler(event.get());
3404     if (event->result().isNull())
3405         return true;
3406
3407     String text = event->result();
3408     text.replace('\\', backslashAsCurrencySymbol());
3409
3410     return [_bridge runBeforeUnloadConfirmPanelWithMessage:text];
3411
3412     END_BLOCK_OBJC_EXCEPTIONS;
3413
3414     return true;
3415 }
3416
3417 void FrameMac::dragSourceMovedTo(const PlatformMouseEvent& event)
3418 {
3419     if (_dragSrc && _dragSrcMayBeDHTML)
3420         // for now we don't care if event handler cancels default behavior, since there is none
3421         dispatchDragSrcEvent(dragEvent, event);
3422 }
3423
3424 void FrameMac::dragSourceEndedAt(const PlatformMouseEvent& event, NSDragOperation operation)
3425 {
3426     if (_dragSrc && _dragSrcMayBeDHTML) {
3427         _dragClipboard->setDestinationOperation(operation);
3428         // for now we don't care if event handler cancels default behavior, since there is none
3429         dispatchDragSrcEvent(dragendEvent, event);
3430     }
3431     freeClipboard();
3432     _dragSrc = 0;
3433 }
3434
3435 // returns if we should continue "default processing", i.e., whether eventhandler canceled
3436 bool FrameMac::dispatchDragSrcEvent(const AtomicString &eventType, const PlatformMouseEvent& event) const
3437 {
3438     bool noDefaultProc = d->m_view->dispatchDragEvent(eventType, _dragSrc.get(), event, _dragClipboard.get());
3439     return !noDefaultProc;
3440 }
3441
3442 void Frame::setNeedsReapplyStyles()
3443 {
3444     [Mac(this)->bridge() setNeedsReapplyStyles];
3445 }
3446
3447 }