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