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