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