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