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