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