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