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