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