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