4d33ef0c0ce5b6e187738dc6791ab77c5e4c90d3
[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 "FrameLoadRequest.h"
47 #import "GraphicsContext.h"
48 #import "HTMLDocument.h"
49 #import "HTMLFormElement.h"
50 #import "HTMLFrameElement.h"
51 #import "HTMLGenericFormElement.h"
52 #import "HTMLInputElement.h"
53 #import "HTMLNames.h"
54 #import "HTMLTableCellElement.h"
55 #import "WebCoreEditCommand.h"
56 #import "FormDataMac.h"
57 #import "WebCorePageState.h"
58 #import "Logging.h"
59 #import "MouseEventWithHitTestResults.h"
60 #import "PlatformKeyboardEvent.h"
61 #import "PlatformScrollBar.h"
62 #import "PlatformWheelEvent.h"
63 #import "Plugin.h"
64 #import "RegularExpression.h"
65 #import "RenderImage.h"
66 #import "RenderListItem.h"
67 #import "RenderPart.h"
68 #import "RenderTableCell.h"
69 #import "RenderTheme.h"
70 #import "RenderView.h"
71 #import "TextIterator.h"
72 #import "ResourceLoader.h"
73 #import "WebCoreFrameBridge.h"
74 #import "WebCoreViewFactory.h"
75 #import "WebDashboardRegion.h"
76 #import "WebScriptObjectPrivate.h"
77 #import "csshelper.h"
78 #import "htmlediting.h"
79 #import "kjs_window.h"
80 #import "visible_units.h"
81 #import "WebCoreSystemInterface.h"
82 #import <Carbon/Carbon.h>
83 #import <JavaScriptCore/NP_jsobject.h>
84 #import <JavaScriptCore/npruntime_impl.h>
85
86 #undef _webcore_TIMING
87
88 @interface NSObject (WebPlugIn)
89 - (id)objectForWebScript;
90 - (NPObject *)createPluginScriptableObject;
91 @end
92
93 using namespace std;
94 using namespace KJS::Bindings;
95
96 using KJS::JSLock;
97 using KJS::PausedTimeouts;
98 using KJS::SavedBuiltins;
99 using KJS::SavedProperties;
100
101 namespace WebCore {
102
103 using namespace EventNames;
104 using namespace HTMLNames;
105
106 NSEvent* FrameMac::_currentEvent = nil;
107
108 static NSMutableDictionary* createNSDictionary(const HashMap<String, String>& map)
109 {
110     NSMutableDictionary* dict = [[NSMutableDictionary alloc] initWithCapacity:map.size()];
111     HashMap<String, String>::const_iterator end = map.end();
112     for (HashMap<String, String>::const_iterator it = map.begin(); it != end; ++it) {
113         NSString* key = it->first;
114         NSString* object = it->second;
115         [dict setObject:object forKey:key];
116     }
117     return dict;
118 }
119
120 static const unsigned int escChar = 27;
121 static SEL selectorForKeyEvent(const PlatformKeyboardEvent* event)
122 {
123     // FIXME: This helper function is for the autofill code so the bridge can pass a selector to the form delegate.  
124     // Eventually, we should move all of the autofill code down to WebKit and remove the need for this function by
125     // not relying on the selector in the new implementation.
126     String key = event->unmodifiedText();
127     if (key.length() != 1)
128         return 0;
129
130     SEL selector = NULL;
131     switch (key[0U]) {
132     case NSUpArrowFunctionKey:
133         selector = @selector(moveUp:); break;
134     case NSDownArrowFunctionKey:
135         selector = @selector(moveDown:); break;
136     case escChar:
137         selector = @selector(cancel:); break;
138     case NSTabCharacter:
139         selector = @selector(insertTab:); break;
140     case NSBackTabCharacter:
141         selector = @selector(insertBacktab:); break;
142     case NSNewlineCharacter:
143     case NSCarriageReturnCharacter:
144     case NSEnterCharacter:
145         selector = @selector(insertNewline:); break;
146         break;
147     }
148     return selector;
149 }
150
151 FrameMac::FrameMac(Page* page, Element* ownerElement)
152     : Frame(page, ownerElement)
153     , _bridge(nil)
154     , _mouseDownView(nil)
155     , _sendingEventToSubview(false)
156     , _mouseDownMayStartSelect(false)
157     , _activationEventNumber(0)
158     , _bindingRoot(0)
159     , _windowScriptObject(0)
160     , _windowScriptNPObject(0)
161 {
162     d->m_extension = new BrowserExtensionMac(this);
163 }
164
165 FrameMac::~FrameMac()
166 {
167     setView(0);
168     freeClipboard();
169     clearRecordedFormValues();    
170     
171     [_bridge clearFrame];
172     HardRelease(_bridge);
173     _bridge = nil;
174
175     cancelAndClear();
176 }
177
178 void FrameMac::freeClipboard()
179 {
180     if (_dragClipboard)
181         _dragClipboard->setAccessPolicy(ClipboardMac::Numb);
182 }
183
184 bool FrameMac::openURL(const KURL &url)
185 {
186     BEGIN_BLOCK_OBJC_EXCEPTIONS;
187
188     // FIXME: The lack of args here to get the reload flag from
189     // indicates a problem in how we use Frame::processObjectRequest,
190     // where we are opening the URL before the args are set up.
191     [_bridge loadURL:url.getNSURL()
192             referrer:[_bridge referrer]
193               reload:NO
194          userGesture:userGestureHint()
195               target:nil
196      triggeringEvent:nil
197                 form:nil
198           formValues:nil];
199
200     END_BLOCK_OBJC_EXCEPTIONS;
201
202     return true;
203 }
204
205 // Either get cached regexp or build one that matches any of the labels.
206 // The regexp we build is of the form:  (STR1|STR2|STRN)
207 RegularExpression *regExpForLabels(NSArray *labels)
208 {
209     // All the ObjC calls in this method are simple array and string
210     // calls which we can assume do not raise exceptions
211
212
213     // Parallel arrays that we use to cache regExps.  In practice the number of expressions
214     // that the app will use is equal to the number of locales is used in searching.
215     static const unsigned int regExpCacheSize = 4;
216     static NSMutableArray *regExpLabels = nil;
217     static Vector<RegularExpression*> regExps;
218     static RegularExpression wordRegExp = RegularExpression("\\w");
219
220     RegularExpression *result;
221     if (!regExpLabels)
222         regExpLabels = [[NSMutableArray alloc] initWithCapacity:regExpCacheSize];
223     CFIndex cacheHit = [regExpLabels indexOfObject:labels];
224     if (cacheHit != NSNotFound)
225         result = regExps.at(cacheHit);
226     else {
227         DeprecatedString pattern("(");
228         unsigned int numLabels = [labels count];
229         unsigned int i;
230         for (i = 0; i < numLabels; i++) {
231             DeprecatedString label = DeprecatedString::fromNSString((NSString *)[labels objectAtIndex:i]);
232
233             bool startsWithWordChar = false;
234             bool endsWithWordChar = false;
235             if (label.length() != 0) {
236                 startsWithWordChar = wordRegExp.search(label.at(0)) >= 0;
237                 endsWithWordChar = wordRegExp.search(label.at(label.length() - 1)) >= 0;
238             }
239             
240             if (i != 0)
241                 pattern.append("|");
242             // Search for word boundaries only if label starts/ends with "word characters".
243             // If we always searched for word boundaries, this wouldn't work for languages
244             // such as Japanese.
245             if (startsWithWordChar) {
246                 pattern.append("\\b");
247             }
248             pattern.append(label);
249             if (endsWithWordChar) {
250                 pattern.append("\\b");
251             }
252         }
253         pattern.append(")");
254         result = new RegularExpression(pattern, false);
255     }
256
257     // add regexp to the cache, making sure it is at the front for LRU ordering
258     if (cacheHit != 0) {
259         if (cacheHit != NSNotFound) {
260             // remove from old spot
261             [regExpLabels removeObjectAtIndex:cacheHit];
262             regExps.remove(cacheHit);
263         }
264         // add to start
265         [regExpLabels insertObject:labels atIndex:0];
266         regExps.insert(0, result);
267         // trim if too big
268         if ([regExpLabels count] > regExpCacheSize) {
269             [regExpLabels removeObjectAtIndex:regExpCacheSize];
270             RegularExpression *last = regExps.last();
271             regExps.removeLast();
272             delete last;
273         }
274     }
275     return result;
276 }
277
278 NSString* FrameMac::searchForLabelsAboveCell(RegularExpression* regExp, HTMLTableCellElement* cell)
279 {
280     RenderTableCell* cellRenderer = static_cast<RenderTableCell*>(cell->renderer());
281
282     if (cellRenderer && cellRenderer->isTableCell()) {
283         RenderTableCell* cellAboveRenderer = cellRenderer->table()->cellAbove(cellRenderer);
284
285         if (cellAboveRenderer) {
286             HTMLTableCellElement *aboveCell =
287                 static_cast<HTMLTableCellElement*>(cellAboveRenderer->element());
288
289             if (aboveCell) {
290                 // search within the above cell we found for a match
291                 for (Node *n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) {
292                     if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
293                         // For each text chunk, run the regexp
294                         DeprecatedString nodeString = n->nodeValue().deprecatedString();
295                         int pos = regExp->searchRev(nodeString);
296                         if (pos >= 0)
297                             return nodeString.mid(pos, regExp->matchedLength()).getNSString();
298                     }
299                 }
300             }
301         }
302     }
303     // Any reason in practice to search all cells in that are above cell?
304     return nil;
305 }
306
307 NSString *FrameMac::searchForLabelsBeforeElement(NSArray *labels, Element *element)
308 {
309     RegularExpression *regExp = regExpForLabels(labels);
310     // We stop searching after we've seen this many chars
311     const unsigned int charsSearchedThreshold = 500;
312     // This is the absolute max we search.  We allow a little more slop than
313     // charsSearchedThreshold, to make it more likely that we'll search whole nodes.
314     const unsigned int maxCharsSearched = 600;
315     // If the starting element is within a table, the cell that contains it
316     HTMLTableCellElement *startingTableCell = 0;
317     bool searchedCellAbove = false;
318
319     // walk backwards in the node tree, until another element, or form, or end of tree
320     int unsigned lengthSearched = 0;
321     Node *n;
322     for (n = element->traversePreviousNode();
323          n && lengthSearched < charsSearchedThreshold;
324          n = n->traversePreviousNode())
325     {
326         if (n->hasTagName(formTag)
327             || (n->isHTMLElement()
328                 && static_cast<HTMLElement*>(n)->isGenericFormElement()))
329         {
330             // We hit another form element or the start of the form - bail out
331             break;
332         } else if (n->hasTagName(tdTag) && !startingTableCell) {
333             startingTableCell = static_cast<HTMLTableCellElement*>(n);
334         } else if (n->hasTagName(trTag) && startingTableCell) {
335             NSString *result = searchForLabelsAboveCell(regExp, startingTableCell);
336             if (result) {
337                 return result;
338             }
339             searchedCellAbove = true;
340         } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
341             // For each text chunk, run the regexp
342             DeprecatedString nodeString = n->nodeValue().deprecatedString();
343             // add 100 for slop, to make it more likely that we'll search whole nodes
344             if (lengthSearched + nodeString.length() > maxCharsSearched)
345                 nodeString = nodeString.right(charsSearchedThreshold - lengthSearched);
346             int pos = regExp->searchRev(nodeString);
347             if (pos >= 0)
348                 return nodeString.mid(pos, regExp->matchedLength()).getNSString();
349             else
350                 lengthSearched += nodeString.length();
351         }
352     }
353
354     // If we started in a cell, but bailed because we found the start of the form or the
355     // previous element, we still might need to search the row above us for a label.
356     if (startingTableCell && !searchedCellAbove) {
357          return searchForLabelsAboveCell(regExp, startingTableCell);
358     } else {
359         return nil;
360     }
361 }
362
363 NSString *FrameMac::matchLabelsAgainstElement(NSArray *labels, Element *element)
364 {
365     DeprecatedString name = element->getAttribute(nameAttr).deprecatedString();
366     // Make numbers and _'s in field names behave like word boundaries, e.g., "address2"
367     name.replace(RegularExpression("[[:digit:]]"), " ");
368     name.replace('_', ' ');
369     
370     RegularExpression *regExp = regExpForLabels(labels);
371     // Use the largest match we can find in the whole name string
372     int pos;
373     int length;
374     int bestPos = -1;
375     int bestLength = -1;
376     int start = 0;
377     do {
378         pos = regExp->search(name, start);
379         if (pos != -1) {
380             length = regExp->matchedLength();
381             if (length >= bestLength) {
382                 bestPos = pos;
383                 bestLength = length;
384             }
385             start = pos+1;
386         }
387     } while (pos != -1);
388
389     if (bestPos != -1)
390         return name.mid(bestPos, bestLength).getNSString();
391     return nil;
392 }
393
394 void FrameMac::submitForm(const FrameLoadRequest& request)
395 {
396     BEGIN_BLOCK_OBJC_EXCEPTIONS;
397
398     // FIXME: We'd like to remove this altogether and fix the multiple form submission issue another way.
399     // We do not want to submit more than one form from the same page,
400     // nor do we want to submit a single form more than once.
401     // This flag prevents these from happening; not sure how other browsers prevent this.
402     // The flag is reset in each time we start handle a new mouse or key down event, and
403     // also in setView since this part may get reused for a page from the back/forward cache.
404     // The form multi-submit logic here is only needed when we are submitting a form that affects this frame.
405     // FIXME: Frame targeting is only one of the ways the submission could end up doing something other
406     // than replacing this frame's content, so this check is flawed. On the other hand, the check is hardly
407     // needed any more now that we reset d->m_submittedFormURL on each mouse or key down event.
408     WebCoreFrameBridge *target = request.m_frameName.isEmpty() ? _bridge : [_bridge findFrameNamed:request.m_frameName];
409     Frame *targetPart = [target impl];
410     bool willReplaceThisFrame = false;
411     for (Frame *p = this; p; p = p->tree()->parent()) {
412         if (p == targetPart) {
413             willReplaceThisFrame = true;
414             break;
415         }
416     }
417     if (willReplaceThisFrame) {
418         if (d->m_submittedFormURL == request.m_request.url())
419             return;
420         d->m_submittedFormURL = request.m_request.url();
421     }
422
423     ObjCDOMElement* submitForm = [DOMElement _elementWith:d->m_formAboutToBeSubmitted.get()];
424     NSMutableDictionary* formValues = createNSDictionary(d->m_formValuesAboutToBeSubmitted);
425     
426     if (request.m_request.httpMethod() != "POST") {
427         [_bridge loadURL:request.m_request.url().getNSURL()
428                 referrer:[_bridge referrer]
429                   reload:request.m_request.cachePolicy() == ReloadIgnoringCacheData
430              userGesture:true
431                   target:request.m_frameName
432          triggeringEvent:_currentEvent
433                     form:submitForm
434               formValues:formValues];
435     } else {
436         [_bridge postWithURL:request.m_request.url().getNSURL()
437                     referrer:[_bridge referrer] 
438                       target:request.m_frameName
439                         data:arrayFromFormData(request.m_request.httpBody())
440                  contentType:request.m_request.httpContentType()
441              triggeringEvent:_currentEvent
442                         form:submitForm
443                   formValues:formValues];
444     }
445     [formValues release];
446     clearRecordedFormValues();
447
448     END_BLOCK_OBJC_EXCEPTIONS;
449 }
450
451 void FrameMac::frameDetached()
452 {
453     Frame::frameDetached();
454
455     BEGIN_BLOCK_OBJC_EXCEPTIONS;
456     [Mac(this)->bridge() frameDetached];
457     END_BLOCK_OBJC_EXCEPTIONS;
458 }
459
460 void FrameMac::openURLRequest(const FrameLoadRequest& request)
461 {
462     BEGIN_BLOCK_OBJC_EXCEPTIONS;
463     
464     NSString *referrer;
465     String argsReferrer = request.m_request.httpReferrer();
466     if (!argsReferrer.isEmpty())
467         referrer = argsReferrer;
468     else
469         referrer = [_bridge referrer];
470     
471     [_bridge loadURL:request.m_request.url().getNSURL()
472             referrer:referrer
473               reload:request.m_request.cachePolicy() == ReloadIgnoringCacheData
474          userGesture:userGestureHint()
475               target:request.m_frameName
476      triggeringEvent:nil
477                 form:nil
478           formValues:nil];
479     
480     END_BLOCK_OBJC_EXCEPTIONS;
481 }
482
483
484 void FrameMac::urlSelected(const FrameLoadRequest& request)
485 {
486     BEGIN_BLOCK_OBJC_EXCEPTIONS;
487
488     NSString* referrer;
489     String argsReferrer = request.m_request.httpReferrer();
490     if (!argsReferrer.isEmpty())
491         referrer = argsReferrer;
492     else
493         referrer = [_bridge referrer];
494
495     [_bridge loadURL:request.m_request.url().getNSURL()
496             referrer:referrer
497               reload:request.m_request.cachePolicy() == ReloadIgnoringCacheData
498          userGesture:true
499               target:request.m_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" while tracking the mouse, because tearing down the
1500     // page while an AppKit control is tracking the mouse can cause a crash.
1501     
1502     // FIXME: In theory, WebCore now tolerates tear-down while tracking the
1503     // mouse. We should confirm that, and then remove the deferrsLoading
1504     // hack entirely.
1505     
1506     BOOL wasDeferringLoading = [_bridge defersLoading];
1507     if (!wasDeferringLoading)
1508         [_bridge setDefersLoading:YES];
1509
1510     ASSERT(!_sendingEventToSubview);
1511     _sendingEventToSubview = true;
1512     [view mouseDown:_currentEvent];
1513     _sendingEventToSubview = false;
1514     
1515     if (!wasDeferringLoading)
1516         [_bridge setDefersLoading:NO];
1517
1518     // Remember which view we sent the event to, so we can direct the release event properly.
1519     _mouseDownView = view;
1520     _mouseDownWasInSubframe = false;
1521
1522     END_BLOCK_OBJC_EXCEPTIONS;
1523
1524     return true;
1525 }
1526
1527 bool FrameMac::lastEventIsMouseUp() const
1528 {
1529     // Many AK widgets run their own event loops and consume events while the mouse is down.
1530     // When they finish, currentEvent is the mouseUp that they exited on.  We need to update
1531     // the khtml state with this mouseUp, which khtml never saw.  This method lets us detect
1532     // that state.
1533
1534     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1535     NSEvent *currentEventAfterHandlingMouseDown = [NSApp currentEvent];
1536     if (_currentEvent != currentEventAfterHandlingMouseDown) {
1537         if ([currentEventAfterHandlingMouseDown type] == NSLeftMouseUp) {
1538             return true;
1539         }
1540     }
1541     END_BLOCK_OBJC_EXCEPTIONS;
1542
1543     return false;
1544 }
1545     
1546 // Note that this does the same kind of check as [target isDescendantOf:superview].
1547 // There are two differences: This is a lot slower because it has to walk the whole
1548 // tree, and this works in cases where the target has already been deallocated.
1549 static bool findViewInSubviews(NSView *superview, NSView *target)
1550 {
1551     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1552     NSEnumerator *e = [[superview subviews] objectEnumerator];
1553     NSView *subview;
1554     while ((subview = [e nextObject])) {
1555         if (subview == target || findViewInSubviews(subview, target)) {
1556             return true;
1557         }
1558     }
1559     END_BLOCK_OBJC_EXCEPTIONS;
1560     
1561     return false;
1562 }
1563
1564 NSView *FrameMac::mouseDownViewIfStillGood()
1565 {
1566     // Since we have no way of tracking the lifetime of _mouseDownView, we have to assume that
1567     // it could be deallocated already. We search for it in our subview tree; if we don't find
1568     // it, we set it to nil.
1569     NSView *mouseDownView = _mouseDownView;
1570     if (!mouseDownView) {
1571         return nil;
1572     }
1573     FrameView *topFrameView = d->m_view.get();
1574     NSView *topView = topFrameView ? topFrameView->getView() : nil;
1575     if (!topView || !findViewInSubviews(topView, mouseDownView)) {
1576         _mouseDownView = nil;
1577         return nil;
1578     }
1579     return mouseDownView;
1580 }
1581
1582 bool FrameMac::eventMayStartDrag(NSEvent *event) const
1583 {
1584     // This is a pre-flight check of whether the event might lead to a drag being started.  Be careful
1585     // that its logic needs to stay in sync with handleMouseMoveEvent() and the way we setMouseDownMayStartDrag
1586     // in handleMousePressEvent
1587     
1588     if ([event type] != NSLeftMouseDown || [event clickCount] != 1) {
1589         return false;
1590     }
1591     
1592     BOOL DHTMLFlag, UAFlag;
1593     [_bridge allowDHTMLDrag:&DHTMLFlag UADrag:&UAFlag];
1594     if (!DHTMLFlag && !UAFlag) {
1595         return false;
1596     }
1597
1598     NSPoint loc = [event locationInWindow];
1599     IntPoint mouseDownPos = d->m_view->windowToContents(IntPoint(loc));
1600     RenderObject::NodeInfo nodeInfo(true, false);
1601     renderer()->layer()->hitTest(nodeInfo, mouseDownPos);
1602     bool srcIsDHTML;
1603     return nodeInfo.innerNode()->renderer()->draggableNode(DHTMLFlag, UAFlag, mouseDownPos.x(), mouseDownPos.y(), srcIsDHTML);
1604 }
1605
1606 // The link drag hysteresis is much larger than the others because there
1607 // needs to be enough space to cancel the link press without starting a link drag,
1608 // and because dragging links is rare.
1609 const float LinkDragHysteresis = 40.0;
1610 const float ImageDragHysteresis = 5.0;
1611 const float TextDragHysteresis = 3.0;
1612 const float GeneralDragHysteresis = 3.0;
1613 const float TextDragDelay = 0.15;
1614
1615 bool FrameMac::dragHysteresisExceeded(float dragLocationX, float dragLocationY) const
1616 {
1617     IntPoint dragViewportLocation((int)dragLocationX, (int)dragLocationY);
1618     IntPoint dragLocation = d->m_view->windowToContents(dragViewportLocation);
1619     IntSize delta = dragLocation - m_mouseDownPos;
1620     
1621     float threshold = GeneralDragHysteresis;
1622     if (_dragSrcIsImage)
1623         threshold = ImageDragHysteresis;
1624     else if (_dragSrcIsLink)
1625         threshold = LinkDragHysteresis;
1626     else if (_dragSrcInSelection)
1627         threshold = TextDragHysteresis;
1628
1629     return fabsf(delta.width()) >= threshold || fabsf(delta.height()) >= threshold;
1630 }
1631
1632 void FrameMac::handleMouseMoveEvent(const MouseEventWithHitTestResults& event)
1633 {
1634     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1635
1636     if ([_currentEvent type] == NSLeftMouseDragged) {
1637         NSView *view = mouseDownViewIfStillGood();
1638
1639         if (view) {
1640             _sendingEventToSubview = true;
1641             [view mouseDragged:_currentEvent];
1642             _sendingEventToSubview = false;
1643             return;
1644         }
1645
1646         // Careful that the drag starting logic stays in sync with eventMayStartDrag()
1647     
1648         if (mouseDownMayStartDrag() && !_dragSrc) {
1649             BOOL tempFlag1, tempFlag2;
1650             [_bridge allowDHTMLDrag:&tempFlag1 UADrag:&tempFlag2];
1651             _dragSrcMayBeDHTML = tempFlag1;
1652             _dragSrcMayBeUA = tempFlag2;
1653             if (!_dragSrcMayBeDHTML && !_dragSrcMayBeUA) {
1654                 setMouseDownMayStartDrag(false);     // no element is draggable
1655             }
1656         }
1657         
1658         if (mouseDownMayStartDrag() && !_dragSrc) {
1659             // try to find an element that wants to be dragged
1660             RenderObject::NodeInfo nodeInfo(true, false);
1661             renderer()->layer()->hitTest(nodeInfo, m_mouseDownPos);
1662             Node *node = nodeInfo.innerNode();
1663             _dragSrc = (node && node->renderer()) ? node->renderer()->draggableNode(_dragSrcMayBeDHTML, _dragSrcMayBeUA, m_mouseDownPos.x(), m_mouseDownPos.y(), _dragSrcIsDHTML) : 0;
1664             if (!_dragSrc) {
1665                 setMouseDownMayStartDrag(false);     // no element is draggable
1666             } else {
1667                 // remember some facts about this source, while we have a NodeInfo handy
1668                 node = nodeInfo.URLElement();
1669                 _dragSrcIsLink = node && node->isLink();
1670
1671                 node = nodeInfo.innerNonSharedNode();
1672                 _dragSrcIsImage = node && node->renderer() && node->renderer()->isImage();
1673                 
1674                 _dragSrcInSelection = isPointInsideSelection(m_mouseDownPos);
1675             }                
1676         }
1677         
1678         // For drags starting in the selection, the user must wait between the mousedown and mousedrag,
1679         // or else we bail on the dragging stuff and allow selection to occur
1680         if (mouseDownMayStartDrag() && _dragSrcInSelection && [_currentEvent timestamp] - _mouseDownTimestamp < TextDragDelay) {
1681             setMouseDownMayStartDrag(false);
1682             // ...but if this was the first click in the window, we don't even want to start selection
1683             if (_activationEventNumber == [_currentEvent eventNumber]) {
1684                 _mouseDownMayStartSelect = false;
1685             }
1686         }
1687
1688         if (mouseDownMayStartDrag()) {
1689             // We are starting a text/image/url drag, so the cursor should be an arrow
1690             d->m_view->setCursor(pointerCursor());
1691             
1692             NSPoint dragLocation = [_currentEvent locationInWindow];
1693             if (dragHysteresisExceeded(dragLocation.x, dragLocation.y)) {
1694                 
1695                 // Once we're past the hysteresis point, we don't want to treat this gesture as a click
1696                 d->m_view->invalidateClick();
1697
1698                 NSImage *dragImage = nil;       // we use these values if WC is out of the loop
1699                 NSPoint dragLoc = NSZeroPoint;
1700                 NSDragOperation srcOp = NSDragOperationNone;                
1701                 BOOL wcWrotePasteboard = NO;
1702                 if (_dragSrcMayBeDHTML) {
1703                     NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
1704                     // Must be done before ondragstart adds types and data to the pboard,
1705                     // also done for security, as it erases data from the last drag
1706                     [pasteboard declareTypes:[NSArray array] owner:nil];
1707                     
1708                     freeClipboard();    // would only happen if we missed a dragEnd.  Do it anyway, just
1709                                         // to make sure it gets numbified
1710                     _dragClipboard = new ClipboardMac(true, pasteboard, ClipboardMac::Writable, this);
1711                     
1712                     // If this is drag of an element, get set up to generate a default image.  Otherwise
1713                     // WebKit will generate the default, the element doesn't override.
1714                     if (_dragSrcIsDHTML) {
1715                         int srcX, srcY;
1716                         _dragSrc->renderer()->absolutePosition(srcX, srcY);
1717                         IntSize delta = m_mouseDownPos - IntPoint(srcX, srcY);
1718                         _dragClipboard->setDragImageElement(_dragSrc.get(), IntPoint() + delta);
1719                     } 
1720
1721                     setMouseDownMayStartDrag(dispatchDragSrcEvent(dragstartEvent, m_mouseDown) && mayCopy());
1722                     // Invalidate clipboard here against anymore pasteboard writing for security.  The drag
1723                     // image can still be changed as we drag, but not the pasteboard data.
1724                     _dragClipboard->setAccessPolicy(ClipboardMac::ImageWritable);
1725                     
1726                     if (mouseDownMayStartDrag()) {
1727                         // gather values from DHTML element, if it set any
1728                         _dragClipboard->sourceOperation(&srcOp);
1729
1730                         NSArray *types = [pasteboard types];
1731                         wcWrotePasteboard = types && [types count] > 0;
1732
1733                         if (_dragSrcMayBeDHTML) {
1734                             dragImage = _dragClipboard->dragNSImage(&dragLoc);
1735                         }
1736                         
1737                         // Yuck, dragSourceMovedTo() can be called as a result of kicking off the drag with
1738                         // dragImage!  Because of that dumb reentrancy, we may think we've not started the
1739                         // drag when that happens.  So we have to assume it's started before we kick it off.
1740                         _dragClipboard->setDragHasStarted();
1741                     }
1742                 }
1743                 
1744                 if (mouseDownMayStartDrag()) {
1745                     BOOL startedDrag = [_bridge startDraggingImage:dragImage at:dragLoc operation:srcOp event:_currentEvent sourceIsDHTML:_dragSrcIsDHTML DHTMLWroteData:wcWrotePasteboard];
1746                     if (!startedDrag && _dragSrcMayBeDHTML) {
1747                         // WebKit canned the drag at the last minute - we owe _dragSrc a DRAGEND event
1748                         PlatformMouseEvent event(PlatformMouseEvent::currentEvent);
1749                         dispatchDragSrcEvent(dragendEvent, event);
1750                         setMouseDownMayStartDrag(false);
1751                     }
1752                 } 
1753
1754                 if (!mouseDownMayStartDrag()) {
1755                     // something failed to start the drag, cleanup
1756                     freeClipboard();
1757                     _dragSrc = 0;
1758                 }
1759             }
1760
1761             // No more default handling (like selection), whether we're past the hysteresis bounds or not
1762             return;
1763         }
1764         if (!mouseDownMayStartSelect() && !mouseDownMayStartAutoscroll())
1765             return;
1766             
1767     } else {
1768         // If we allowed the other side of the bridge to handle a drag
1769         // last time, then m_bMousePressed might still be set. So we
1770         // clear it now to make sure the next move after a drag
1771         // doesn't look like a drag.
1772         d->m_bMousePressed = false;
1773     }
1774
1775     Frame::handleMouseMoveEvent(event);
1776
1777     END_BLOCK_OBJC_EXCEPTIONS;
1778 }
1779
1780 // Returns whether caller should continue with "the default processing", which is the same as 
1781 // the event handler NOT setting the return value to false
1782 bool FrameMac::dispatchCPPEvent(const AtomicString &eventType, ClipboardMac::AccessPolicy policy)
1783 {
1784     Node* target = selectionController()->start().element();
1785     if (!target && document())
1786         target = document()->body();
1787     if (!target)
1788         return true;
1789     if (target->isShadowNode())
1790         target = target->shadowParentNode();
1791     
1792     RefPtr<ClipboardMac> clipboard = new ClipboardMac(false, [NSPasteboard generalPasteboard], (ClipboardMac::AccessPolicy)policy);
1793
1794     ExceptionCode ec = 0;
1795     RefPtr<Event> evt = new ClipboardEvent(eventType, true, true, clipboard.get());
1796     EventTargetNodeCast(target)->dispatchEvent(evt, ec, true);
1797     bool noDefaultProcessing = evt->defaultPrevented();
1798
1799     // invalidate clipboard here for security
1800     clipboard->setAccessPolicy(ClipboardMac::Numb);
1801
1802     return !noDefaultProcessing;
1803 }
1804
1805 // WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items.  They
1806 // also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items.
1807 // We need to use onbeforecopy as a real menu enabler because we allow elements that are not
1808 // normally selectable to implement copy/paste (like divs, or a document body).
1809
1810 bool FrameMac::mayDHTMLCut()
1811 {
1812     return mayCopy() && !dispatchCPPEvent(beforecutEvent, ClipboardMac::Numb);
1813 }
1814
1815 bool FrameMac::mayDHTMLCopy()
1816 {
1817     return mayCopy() && !dispatchCPPEvent(beforecopyEvent, ClipboardMac::Numb);
1818 }
1819
1820 bool FrameMac::mayDHTMLPaste()
1821 {
1822     return !dispatchCPPEvent(beforepasteEvent, ClipboardMac::Numb);
1823 }
1824
1825 bool FrameMac::tryDHTMLCut()
1826 {
1827     if (!mayCopy())
1828         return false;
1829
1830     // Must be done before oncut adds types and data to the pboard,
1831     // also done for security, as it erases data from the last copy/paste.
1832     [[NSPasteboard generalPasteboard] declareTypes:[NSArray array] owner:nil];
1833
1834     return !dispatchCPPEvent(cutEvent, ClipboardMac::Writable);
1835 }
1836
1837 bool FrameMac::tryDHTMLCopy()
1838 {
1839     if (!mayCopy())
1840         return false;
1841
1842     // Must be done before oncopy adds types and data to the pboard,
1843     // also done for security, as it erases data from the last copy/paste.
1844     [[NSPasteboard generalPasteboard] declareTypes:[NSArray array] owner:nil];
1845
1846     return !dispatchCPPEvent(copyEvent, ClipboardMac::Writable);
1847 }
1848
1849 bool FrameMac::tryDHTMLPaste()
1850 {
1851     return !dispatchCPPEvent(pasteEvent, ClipboardMac::Readable);
1852 }
1853
1854 void FrameMac::handleMouseReleaseEvent(const MouseEventWithHitTestResults& event)
1855 {
1856     NSView *view = mouseDownViewIfStillGood();
1857     if (!view) {
1858         // If this was the first click in the window, we don't even want to clear the selection.
1859         // This case occurs when the user clicks on a draggable element, since we have to process
1860         // the mouse down and drag events to see if we might start a drag.  For other first clicks
1861         // in a window, we just don't acceptFirstMouse, and the whole down-drag-up sequence gets
1862         // ignored upstream of this layer.
1863         if (_activationEventNumber != [_currentEvent eventNumber])
1864             Frame::handleMouseReleaseEvent(event);
1865         return;
1866     }
1867     stopAutoscrollTimer();
1868     
1869     _sendingEventToSubview = true;
1870     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1871     [view mouseUp:_currentEvent];
1872     END_BLOCK_OBJC_EXCEPTIONS;
1873     _sendingEventToSubview = false;
1874 }
1875
1876 bool FrameMac::passSubframeEventToSubframe(MouseEventWithHitTestResults& event, Frame* subframePart)
1877 {
1878     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1879
1880     switch ([_currentEvent type]) {
1881         case NSMouseMoved: {
1882             ASSERT(subframePart);
1883             [Mac(subframePart)->bridge() mouseMoved:_currentEvent];
1884             return true;
1885         }
1886         
1887         case NSLeftMouseDown: {
1888             Node *node = event.targetNode();
1889             if (!node) {
1890                 return false;
1891             }
1892             RenderObject *renderer = node->renderer();
1893             if (!renderer || !renderer->isWidget()) {
1894                 return false;
1895             }
1896             Widget *widget = static_cast<RenderWidget*>(renderer)->widget();
1897             if (!widget || !widget->isFrameView())
1898                 return false;
1899             if (!passWidgetMouseDownEventToWidget(static_cast<RenderWidget*>(renderer))) {
1900                 return false;
1901             }
1902             _mouseDownWasInSubframe = true;
1903             return true;
1904         }
1905         case NSLeftMouseUp: {
1906             if (!_mouseDownWasInSubframe) {
1907                 return false;
1908             }
1909             NSView *view = mouseDownViewIfStillGood();
1910             if (!view) {
1911                 return false;
1912             }
1913             ASSERT(!_sendingEventToSubview);
1914             _sendingEventToSubview = true;
1915             [view mouseUp:_currentEvent];
1916             _sendingEventToSubview = false;
1917             return true;
1918         }
1919         case NSLeftMouseDragged: {
1920             if (!_mouseDownWasInSubframe) {
1921                 return false;
1922             }
1923             NSView *view = mouseDownViewIfStillGood();
1924             if (!view) {
1925                 return false;
1926             }
1927             ASSERT(!_sendingEventToSubview);
1928             _sendingEventToSubview = true;
1929             [view mouseDragged:_currentEvent];
1930             _sendingEventToSubview = false;
1931             return true;
1932         }
1933         default:
1934             return false;
1935     }
1936     END_BLOCK_OBJC_EXCEPTIONS;
1937
1938     return false;
1939 }
1940
1941 bool FrameMac::passWheelEventToWidget(Widget* widget)
1942 {
1943     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1944         
1945     if ([_currentEvent type] != NSScrollWheel || _sendingEventToSubview || !widget) 
1946         return false;
1947     else {
1948         NSView *nodeView = widget->getView();
1949         ASSERT(nodeView);
1950         ASSERT([nodeView superview]);
1951         NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[_currentEvent locationInWindow] fromView:nil]];
1952     
1953         ASSERT(view);
1954         _sendingEventToSubview = true;
1955         [view scrollWheel:_currentEvent];
1956         _sendingEventToSubview = false;
1957         return true;
1958     }
1959             
1960     END_BLOCK_OBJC_EXCEPTIONS;
1961     return false;
1962 }
1963
1964 void FrameMac::mouseDown(NSEvent *event)
1965 {
1966     FrameView *v = d->m_view.get();
1967     if (!v || _sendingEventToSubview)
1968         return;
1969
1970     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1971
1972     prepareForUserAction();
1973
1974     _mouseDownView = nil;
1975     _dragSrc = 0;
1976     
1977     NSEvent *oldCurrentEvent = _currentEvent;
1978     _currentEvent = HardRetain(event);
1979     m_mouseDown = PlatformMouseEvent(event);
1980     NSPoint loc = [event locationInWindow];
1981     m_mouseDownPos = d->m_view->windowToContents(IntPoint(loc));
1982     _mouseDownTimestamp = [event timestamp];
1983
1984     setMouseDownMayStartDrag(false);
1985     _mouseDownMayStartSelect = false;
1986     setMouseDownMayStartAutoscroll(false);
1987     
1988     v->handleMousePressEvent(event);
1989     
1990     ASSERT(_currentEvent == event);
1991     HardRelease(event);
1992     _currentEvent = oldCurrentEvent;
1993
1994     END_BLOCK_OBJC_EXCEPTIONS;
1995 }
1996
1997 void FrameMac::mouseDragged(NSEvent *event)
1998 {
1999     FrameView *v = d->m_view.get();
2000     if (!v || _sendingEventToSubview) {
2001         return;
2002     }
2003
2004     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2005
2006     NSEvent *oldCurrentEvent = _currentEvent;
2007     _currentEvent = HardRetain(event);
2008
2009     v->handleMouseMoveEvent(event);
2010     
2011     ASSERT(_currentEvent == event);
2012     HardRelease(event);
2013     _currentEvent = oldCurrentEvent;
2014
2015     END_BLOCK_OBJC_EXCEPTIONS;
2016 }
2017
2018 void FrameMac::mouseUp(NSEvent *event)
2019 {
2020     FrameView *v = d->m_view.get();
2021     if (!v || _sendingEventToSubview)
2022         return;
2023
2024     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2025
2026     NSEvent *oldCurrentEvent = _currentEvent;
2027     _currentEvent = HardRetain(event);
2028
2029     // Our behavior here is a little different that Qt. Qt always sends
2030     // a mouse release event, even for a double click. To correct problems
2031     // in khtml's DOM click event handling we do not send a release here
2032     // for a double click. Instead we send that event from FrameView's
2033     // handleMouseDoubleClickEvent. Note also that the third click of
2034     // a triple click is treated as a single click, but the fourth is then
2035     // treated as another double click. Hence the "% 2" below.
2036     int clickCount = [event clickCount];
2037     if (clickCount > 0 && clickCount % 2 == 0)
2038         v->handleMouseDoubleClickEvent(event);
2039     else
2040         v->handleMouseReleaseEvent(event);
2041     
2042     ASSERT(_currentEvent == event);
2043     HardRelease(event);
2044     _currentEvent = oldCurrentEvent;
2045     
2046     _mouseDownView = nil;
2047
2048     END_BLOCK_OBJC_EXCEPTIONS;
2049 }
2050
2051 /*
2052  A hack for the benefit of AK's PopUpButton, which uses the Carbon menu manager, which thus
2053  eats all subsequent events after it is starts its modal tracking loop.  After the interaction
2054  is done, this routine is used to fix things up.  When a mouse down started us tracking in
2055  the widget, we post a fake mouse up to balance the mouse down we started with. When a 
2056  key down started us tracking in the widget, we post a fake key up to balance things out.
2057  In addition, we post a fake mouseMoved to get the cursor in sync with whatever we happen to 
2058  be over after the tracking is done.
2059  */
2060 void FrameMac::sendFakeEventsAfterWidgetTracking(NSEvent *initiatingEvent)
2061 {
2062     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2063
2064     _sendingEventToSubview = false;
2065     int eventType = [initiatingEvent type];
2066     if (eventType == NSLeftMouseDown || eventType == NSKeyDown) {
2067         NSEvent *fakeEvent = nil;
2068         if (eventType == NSLeftMouseDown) {
2069             fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
2070                                     location:[initiatingEvent locationInWindow]
2071                                 modifierFlags:[initiatingEvent modifierFlags]
2072                                     timestamp:[initiatingEvent timestamp]
2073                                 windowNumber:[initiatingEvent windowNumber]
2074                                         context:[initiatingEvent context]
2075                                     eventNumber:[initiatingEvent eventNumber]
2076                                     clickCount:[initiatingEvent clickCount]
2077                                     pressure:[initiatingEvent pressure]];
2078         
2079             mouseUp(fakeEvent);
2080         }
2081         else { // eventType == NSKeyDown
2082             fakeEvent = [NSEvent keyEventWithType:NSKeyUp
2083                                     location:[initiatingEvent locationInWindow]
2084                                modifierFlags:[initiatingEvent modifierFlags]
2085                                    timestamp:[initiatingEvent timestamp]
2086                                 windowNumber:[initiatingEvent windowNumber]
2087                                      context:[initiatingEvent context]
2088                                   characters:[initiatingEvent characters] 
2089                  charactersIgnoringModifiers:[initiatingEvent charactersIgnoringModifiers] 
2090                                    isARepeat:[initiatingEvent isARepeat] 
2091                                      keyCode:[initiatingEvent keyCode]];
2092             keyEvent(fakeEvent);
2093         }
2094         // FIXME:  We should really get the current modifierFlags here, but there's no way to poll
2095         // them in Cocoa, and because the event stream was stolen by the Carbon menu code we have
2096         // no up-to-date cache of them anywhere.
2097         fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
2098                                        location:[[_bridge window] convertScreenToBase:[NSEvent mouseLocation]]
2099                                   modifierFlags:[initiatingEvent modifierFlags]
2100                                       timestamp:[initiatingEvent timestamp]
2101                                    windowNumber:[initiatingEvent windowNumber]
2102                                         context:[initiatingEvent context]
2103                                     eventNumber:0
2104                                      clickCount:0
2105                                        pressure:0];
2106         mouseMoved(fakeEvent);
2107     }
2108     
2109     END_BLOCK_OBJC_EXCEPTIONS;
2110 }
2111
2112 void FrameMac::mouseMoved(NSEvent *event)
2113 {
2114     FrameView *v = d->m_view.get();
2115     // Reject a mouse moved if the button is down - screws up tracking during autoscroll
2116     // These happen because WebKit sometimes has to fake up moved events.
2117     if (!v || d->m_bMousePressed || _sendingEventToSubview)
2118         return;
2119     
2120     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2121
2122     NSEvent *oldCurrentEvent = _currentEvent;
2123     _currentEvent = HardRetain(event);
2124     
2125     v->handleMouseMoveEvent(event);
2126     
2127     ASSERT(_currentEvent == event);
2128     HardRelease(event);
2129     _currentEvent = oldCurrentEvent;
2130
2131     END_BLOCK_OBJC_EXCEPTIONS;
2132 }
2133
2134 // Called as we walk up the element chain for nodes with CSS property -webkit-user-drag == auto
2135 bool FrameMac::shouldDragAutoNode(Node* node, const IntPoint& point) const
2136 {
2137     // We assume that WebKit only cares about dragging things that can be leaf nodes (text, images, urls).
2138     // This saves a bunch of expensive calls (creating WC and WK element dicts) as we walk farther up
2139     // the node hierarchy, and we also don't have to cook up a way to ask WK about non-leaf nodes
2140     // (since right now WK just hit-tests using a cached lastMouseDown).
2141     if (!node->hasChildNodes() && d->m_view) {
2142         NSPoint eventLoc = d->m_view->contentsToWindow(point);
2143         return [_bridge mayStartDragAtEventLocation:eventLoc];
2144     } else
2145         return NO;
2146 }
2147
2148 bool FrameMac::sendContextMenuEvent(NSEvent *event)
2149 {
2150     Document* doc = d->m_doc.get();
2151     FrameView* v = d->m_view.get();
2152     if (!doc || !v)
2153         return false;
2154
2155     bool swallowEvent;
2156     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2157
2158     NSEvent *oldCurrentEvent = _currentEvent;
2159     _currentEvent = HardRetain(event);
2160     
2161     PlatformMouseEvent mouseEvent(event);
2162
2163     IntPoint viewportPos = v->windowToContents(mouseEvent.pos());
2164     MouseEventWithHitTestResults mev = doc->prepareMouseEvent(false, true, false, viewportPos, mouseEvent);
2165
2166     swallowEvent = v->dispatchMouseEvent(contextmenuEvent, mev.targetNode(), true, 0, mouseEvent, true);
2167     if (!swallowEvent && !isPointInsideSelection(viewportPos) &&
2168             ([_bridge selectWordBeforeMenuEvent] || [_bridge isEditable]
2169                 || (mev.targetNode() && mev.targetNode()->isContentEditable()))) {
2170         _mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection
2171         selectClosestWordFromMouseEvent(mouseEvent, mev.targetNode());
2172     }
2173
2174     ASSERT(_currentEvent == event);
2175     HardRelease(event);
2176     _currentEvent = oldCurrentEvent;
2177
2178     return swallowEvent;
2179
2180     END_BLOCK_OBJC_EXCEPTIONS;
2181
2182     return false;
2183 }
2184
2185 struct ListItemInfo {
2186     unsigned start;
2187     unsigned end;
2188 };
2189
2190 NSFileWrapper *FrameMac::fileWrapperForElement(Element *e)
2191 {
2192     NSFileWrapper *wrapper = nil;
2193     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2194     
2195     const AtomicString& attr = e->getAttribute(srcAttr);
2196     if (!attr.isEmpty()) {
2197         NSURL *URL = completeURL(attr.deprecatedString()).getNSURL();
2198         wrapper = [_bridge fileWrapperForURL:URL];
2199     }    
2200     if (!wrapper) {
2201         RenderImage *renderer = static_cast<RenderImage*>(e->renderer());
2202         if (renderer->cachedImage() && !renderer->cachedImage()->isErrorImage()) {
2203             wrapper = [[NSFileWrapper alloc] initRegularFileWithContents:(NSData*)(renderer->cachedImage()->image()->getTIFFRepresentation())];
2204             [wrapper setPreferredFilename:@"image.tiff"];
2205             [wrapper autorelease];
2206         }
2207     }
2208
2209     return wrapper;
2210
2211     END_BLOCK_OBJC_EXCEPTIONS;
2212
2213     return nil;
2214 }
2215
2216 static Element *listParent(Element *item)
2217 {
2218     while (!item->hasTagName(ulTag) && !item->hasTagName(olTag)) {
2219         item = static_cast<Element*>(item->parentNode());
2220         if (!item)
2221             break;
2222     }
2223     return item;
2224 }
2225
2226 static Node* isTextFirstInListItem(Node *e)
2227 {
2228     if (!e->isTextNode())
2229         return 0;
2230     Node* par = e->parentNode();
2231     while (par) {
2232         if (par->firstChild() != e)
2233             return 0;
2234         if (par->hasTagName(liTag))
2235             return par;
2236         e = par;
2237         par = par->parentNode();
2238     }
2239     return 0;
2240 }
2241
2242 // FIXME: Enhance TextIterator to optionally add attributes, then just call through to that.
2243
2244 #define BULLET_CHAR 0x2022
2245 #define SQUARE_CHAR 0x25AA
2246 #define CIRCLE_CHAR 0x25E6
2247
2248 NSAttributedString *FrameMac::attributedString(Node *startNode, int startOffset, Node *endNode, int endOffset)
2249 {
2250     ListItemInfo info;
2251     NSMutableAttributedString *result;
2252     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2253
2254     Range range(document(), startNode, startOffset, endNode, endOffset);
2255     if (!range.boundaryPointsValid())
2256         return nil;
2257     
2258     Node* firstNode = range.startNode();
2259     if (!firstNode)
2260         return nil;
2261     Node* pastEndNode = range.pastEndNode();
2262     
2263     result = [[[NSMutableAttributedString alloc] init] autorelease];
2264     
2265     bool hasNewLine = true;
2266     bool addedSpace = true;
2267     NSAttributedString *pendingStyledSpace = nil;
2268     bool hasParagraphBreak = true;
2269     const Element *linkStartNode = 0;
2270     unsigned linkStartLocation = 0;
2271     Vector<Element*> listItems;
2272     Vector<ListItemInfo> listItemLocations;
2273     float maxMarkerWidth = 0;
2274     
2275     Node *currentNode = firstNode;
2276     
2277     // If the first item is the entire text of a list item, use the list item node as the start of the 
2278     // selection, not the text node.  The user's intent was probably to select the list.
2279     if (currentNode->isTextNode() && startOffset == 0) {
2280         Node *startListNode = isTextFirstInListItem(firstNode);
2281         if (startListNode){
2282             firstNode = startListNode;
2283             currentNode = firstNode;
2284         }
2285     }
2286     
2287     while (currentNode && currentNode != pastEndNode) {
2288         RenderObject *renderer = currentNode->renderer();
2289         if (renderer) {
2290             RenderStyle *style = renderer->style();
2291             NSFont *font = style->font().primaryFont()->getNSFont();
2292             bool needSpace = pendingStyledSpace != nil;
2293             if (currentNode->isTextNode()) {
2294                 if (hasNewLine) {
2295                     addedSpace = true;
2296                     needSpace = false;
2297                     [pendingStyledSpace release];
2298                     pendingStyledSpace = nil;
2299                     hasNewLine = false;
2300                 }
2301                 DeprecatedString text;
2302                 DeprecatedString str = currentNode->nodeValue().deprecatedString();
2303                 int start = (currentNode == firstNode) ? startOffset : -1;
2304                 int end = (currentNode == endNode) ? endOffset : -1;
2305                 if (renderer->isText()) {
2306                     if (!style->collapseWhiteSpace()) {
2307                         if (needSpace && !addedSpace) {
2308                             if (text.isEmpty() && linkStartLocation == [result length])
2309                                 ++linkStartLocation;
2310                             [result appendAttributedString:pendingStyledSpace];
2311                         }
2312                         int runStart = (start == -1) ? 0 : start;
2313                         int runEnd = (end == -1) ? str.length() : end;
2314                         text += str.mid(runStart, runEnd-runStart);
2315                         [pendingStyledSpace release];
2316                         pendingStyledSpace = nil;
2317                         addedSpace = u_charDirection(str[runEnd - 1].unicode()) == U_WHITE_SPACE_NEUTRAL;
2318                     }
2319                     else {
2320                         RenderText* textObj = static_cast<RenderText*>(renderer);
2321                         if (!textObj->firstTextBox() && str.length() > 0 && !addedSpace) {
2322                             // We have no runs, but we do have a length.  This means we must be
2323                             // whitespace that collapsed away at the end of a line.
2324                             text += ' ';
2325                             addedSpace = true;
2326                         }
2327                         else {
2328                             addedSpace = false;
2329                             for (InlineTextBox* box = textObj->firstTextBox(); box; box = box->nextTextBox()) {
2330                                 int runStart = (start == -1) ? box->m_start : start;
2331                                 int runEnd = (end == -1) ? box->m_start + box->m_len : end;
2332                                 runEnd = min(runEnd, box->m_start + box->m_len);
2333                                 if (runStart >= box->m_start &&
2334                                     runStart < box->m_start + box->m_len) {
2335                                     if (box == textObj->firstTextBox() && box->m_start == runStart && runStart > 0)
2336                                         needSpace = true; // collapsed space at the start
2337                                     if (needSpace && !addedSpace) {
2338                                         if (pendingStyledSpace != nil) {
2339                                             if (text.isEmpty() && linkStartLocation == [result length])
2340                                                 ++linkStartLocation;
2341                                             [result appendAttributedString:pendingStyledSpace];
2342                                         } else
2343                                             text += ' ';
2344                                     }
2345                                     DeprecatedString runText = str.mid(runStart, runEnd - runStart);
2346                                     runText.replace('\n', ' ');
2347                                     text += runText;
2348                                     int nextRunStart = box->nextTextBox() ? box->nextTextBox()->m_start : str.length(); // collapsed space between runs or at the end
2349                                     needSpace = nextRunStart > runEnd;
2350                                     [pendingStyledSpace release];
2351                                     pendingStyledSpace = nil;
2352                                     addedSpace = u_charDirection(str[runEnd - 1].unicode()) == U_WHITE_SPACE_NEUTRAL;
2353                                     start = -1;
2354                                 }
2355                                 if (end != -1 && runEnd >= end)
2356                                     break;
2357                             }
2358                         }
2359                     }
2360                 }
2361                 
2362                 text.replace('\\', renderer->backslashAsCurrencySymbol());
2363     
2364                 if (text.length() > 0 || needSpace) {
2365                     NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init];
2366                     [attrs setObject:font forKey:NSFontAttributeName];
2367                     if (style && style->color().isValid() && style->color().alpha() != 0)
2368                         [attrs setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2369                     if (style && style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0)
2370                         [attrs setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2371
2372                     if (text.length() > 0) {
2373                         hasParagraphBreak = false;
2374                         NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString() attributes:attrs];
2375                         [result appendAttributedString: partialString];                
2376                         [partialString release];
2377                     }
2378
2379                     if (needSpace) {
2380                         [pendingStyledSpace release];
2381                         pendingStyledSpace = [[NSAttributedString alloc] initWithString:@" " attributes:attrs];
2382                     }
2383
2384                     [attrs release];
2385                 }
2386             } else {
2387                 // This is our simple HTML -> ASCII transformation:
2388                 DeprecatedString text;
2389                 if (currentNode->hasTagName(aTag)) {
2390                     // Note the start of the <a> element.  We will add the NSLinkAttributeName
2391                     // attribute to the attributed string when navigating to the next sibling 
2392                     // of this node.
2393                     linkStartLocation = [result length];
2394                     linkStartNode = static_cast<Element*>(currentNode);
2395                 } else if (currentNode->hasTagName(brTag)) {
2396                     text += "\n";
2397                     hasNewLine = true;
2398                 } else if (currentNode->hasTagName(liTag)) {
2399                     DeprecatedString listText;
2400                     Element *itemParent = listParent(static_cast<Element*>(currentNode));
2401                     
2402                     if (!hasNewLine)
2403                         listText += '\n';
2404                     hasNewLine = true;
2405
2406                     listItems.append(static_cast<Element*>(currentNode));
2407                     info.start = [result length];
2408                     info.end = 0;
2409                     listItemLocations.append (info);
2410                     
2411                     listText += '\t';
2412                     if (itemParent && renderer->isListItem()) {
2413                         RenderListItem* listRenderer = static_cast<RenderListItem*>(renderer);
2414
2415                         maxMarkerWidth = MAX([font pointSize], maxMarkerWidth);
2416                         switch(style->listStyleType()) {
2417                             case DISC:
2418                                 listText += ((DeprecatedChar)BULLET_CHAR);
2419                                 break;
2420                             case CIRCLE:
2421                                 listText += ((DeprecatedChar)CIRCLE_CHAR);
2422                                 break;
2423                             case SQUARE:
2424                                 listText += ((DeprecatedChar)SQUARE_CHAR);
2425                                 break;
2426                             case LNONE:
2427                                 break;
2428                             default:
2429                                 DeprecatedString marker = listRenderer->markerStringValue();
2430                                 listText += marker;
2431                                 // Use AppKit metrics.  Will be rendered by AppKit.
2432                                 float markerWidth = [marker.getNSString() sizeWithAttributes:[NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]].width;
2433                                 maxMarkerWidth = MAX(markerWidth, maxMarkerWidth);
2434                         }
2435
2436                         listText += ' ';
2437                         listText += '\t';
2438
2439                         NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init];
2440                         [attrs setObject:font forKey:NSFontAttributeName];
2441                         if (style && style->color().isValid())
2442                             [attrs setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2443                         if (style && style->backgroundColor().isValid())
2444                             [attrs setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2445
2446                         NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:listText.getNSString() attributes:attrs];
2447                         [attrs release];
2448                         [result appendAttributedString: partialString];                
2449                         [partialString release];
2450                     }
2451                 } else if (currentNode->hasTagName(olTag) || currentNode->hasTagName(ulTag)) {
2452                     if (!hasNewLine)
2453                         text += "\n";
2454                     hasNewLine = true;
2455                 } else if (currentNode->hasTagName(blockquoteTag)
2456                         || currentNode->hasTagName(ddTag)
2457                         || currentNode->hasTagName(divTag)
2458                         || currentNode->hasTagName(dlTag)
2459                         || currentNode->hasTagName(dtTag)
2460                         || currentNode->hasTagName(hrTag)
2461                         || currentNode->hasTagName(listingTag)
2462                         || currentNode->hasTagName(preTag)
2463                         || currentNode->hasTagName(tdTag)
2464                         || currentNode->hasTagName(thTag)) {
2465                     if (!hasNewLine)
2466                         text += '\n';
2467                     hasNewLine = true;
2468                 } else if (currentNode->hasTagName(h1Tag)
2469                         || currentNode->hasTagName(h2Tag)
2470                         || currentNode->hasTagName(h3Tag)
2471                         || currentNode->hasTagName(h4Tag)
2472                         || currentNode->hasTagName(h5Tag)
2473                         || currentNode->hasTagName(h6Tag)
2474                         || currentNode->hasTagName(pTag)
2475                         || currentNode->hasTagName(trTag)) {
2476                     if (!hasNewLine)
2477                         text += '\n';
2478                     
2479                     // In certain cases, emit a paragraph break.
2480                     int bottomMargin = renderer->collapsedMarginBottom();
2481                     int fontSize = style->fontDescription().computedPixelSize();
2482                     if (bottomMargin * 2 >= fontSize) {
2483                         if (!hasParagraphBreak) {
2484                             text += '\n';
2485                             hasParagraphBreak = true;
2486                         }
2487                     }
2488                     
2489                     hasNewLine = true;
2490                 }
2491                 else if (currentNode->hasTagName(imgTag)) {
2492                     if (pendingStyledSpace != nil) {
2493                         if (linkStartLocation == [result length])
2494                             ++linkStartLocation;
2495                         [result appendAttributedString:pendingStyledSpace];
2496                         [pendingStyledSpace release];
2497                         pendingStyledSpace = nil;
2498                     }
2499                     NSFileWrapper *fileWrapper = fileWrapperForElement(static_cast<Element*>(currentNode));
2500                     NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWrapper:fileWrapper];
2501                     NSAttributedString *iString = [NSAttributedString attributedStringWithAttachment:attachment];
2502                     [result appendAttributedString: iString];
2503                     [attachment release];
2504                 }
2505
2506                 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()];
2507                 [result appendAttributedString: partialString];
2508                 [partialString release];
2509             }
2510         }
2511
2512         Node *nextNode = currentNode->firstChild();
2513         if (!nextNode)
2514             nextNode = currentNode->nextSibling();
2515
2516         while (!nextNode && currentNode->parentNode()) {
2517             DeprecatedString text;
2518             currentNode = currentNode->parentNode();
2519             if (currentNode == pastEndNode)
2520                 break;
2521             nextNode = currentNode->nextSibling();
2522
2523             if (currentNode->hasTagName(aTag)) {
2524                 // End of a <a> element.  Create an attributed string NSLinkAttributeName attribute
2525                 // for the range of the link.  Note that we create the attributed string from the DOM, which
2526                 // will have corrected any illegally nested <a> elements.
2527                 if (linkStartNode && currentNode == linkStartNode) {
2528                     String href = parseURL(linkStartNode->getAttribute(hrefAttr));
2529                     KURL kURL = Mac(linkStartNode->document()->frame())->completeURL(href.deprecatedString());
2530                     
2531                     NSURL *URL = kURL.getNSURL();
2532                     NSRange tempRange = { linkStartLocation, [result length]-linkStartLocation }; // workaround for 4213314
2533                     [result addAttribute:NSLinkAttributeName value:URL range:tempRange];
2534                     linkStartNode = 0;
2535                 }
2536             }
2537             else if (currentNode->hasTagName(olTag) || currentNode->hasTagName(ulTag)) {
2538                 if (!hasNewLine)
2539                     text += '\n';
2540                 hasNewLine = true;
2541             } else if (currentNode->hasTagName(liTag)) {
2542                 
2543                 int i, count = listItems.size();
2544                 for (i = 0; i < count; i++){
2545                     if (listItems[i] == currentNode){
2546                         listItemLocations[i].end = [result length];
2547                         break;
2548                     }
2549                 }
2550                 if (!hasNewLine)
2551                     text += '\n';
2552                 hasNewLine = true;
2553             } else if (currentNode->hasTagName(blockquoteTag) ||
2554                        currentNode->hasTagName(ddTag) ||
2555                        currentNode->hasTagName(divTag) ||
2556                        currentNode->hasTagName(dlTag) ||
2557                        currentNode->hasTagName(dtTag) ||
2558                        currentNode->hasTagName(hrTag) ||
2559                        currentNode->hasTagName(listingTag) ||
2560                        currentNode->hasTagName(preTag) ||
2561                        currentNode->hasTagName(tdTag) ||
2562                        currentNode->hasTagName(thTag)) {
2563                 if (!hasNewLine)
2564                     text += '\n';
2565                 hasNewLine = true;
2566             } else if (currentNode->hasTagName(pTag) ||
2567                        currentNode->hasTagName(trTag) ||
2568                        currentNode->hasTagName(h1Tag) ||
2569                        currentNode->hasTagName(h2Tag) ||
2570                        currentNode->hasTagName(h3Tag) ||
2571                        currentNode->hasTagName(h4Tag) ||
2572                        currentNode->hasTagName(h5Tag) ||
2573                        currentNode->hasTagName(h6Tag)) {
2574                 if (!hasNewLine)
2575                     text += '\n';
2576                 // An extra newline is needed at the start, not the end, of these types of tags,
2577                 // so don't add another here.
2578                 hasNewLine = true;
2579             }
2580             
2581             NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()];
2582             [result appendAttributedString:partialString];
2583             [partialString release];
2584         }
2585
2586         currentNode = nextNode;
2587     }
2588     
2589     [pendingStyledSpace release];
2590     
2591     // Apply paragraph styles from outside in.  This ensures that nested lists correctly
2592     // override their parent's paragraph style.
2593     {
2594         unsigned i, count = listItems.size();
2595         Element *e;
2596
2597 #ifdef POSITION_LIST
2598         Node *containingBlock;
2599         int containingBlockX, containingBlockY;
2600         
2601         // Determine the position of the outermost containing block.  All paragraph
2602         // styles and tabs should be relative to this position.  So, the horizontal position of 
2603         // each item in the list (in the resulting attributed string) will be relative to position 
2604         // of the outermost containing block.
2605         if (count > 0){
2606             containingBlock = firstNode;
2607             while (containingBlock->renderer()->isInline()){
2608                 containingBlock = containingBlock->parentNode();
2609             }
2610             containingBlock->renderer()->absolutePosition(containingBlockX, containingBlockY);
2611         }
2612 #endif
2613         
2614         for (i = 0; i < count; i++){
2615             e = listItems[i];
2616             info = listItemLocations[i];
2617             
2618             if (info.end < info.start)
2619                 info.end = [result length];
2620                 
2621             RenderObject *r = e->renderer();
2622             RenderStyle *style = r->style();
2623
2624             int rx;
2625             NSFont *font = style->font().primaryFont()->getNSFont();
2626             float pointSize = [font pointSize];
2627
2628 #ifdef POSITION_LIST
2629             int ry;
2630             r->absolutePosition(rx, ry);
2631             rx -= containingBlockX;
2632             
2633             // Ensure that the text is indented at least enough to allow for the markers.
2634             rx = MAX(rx, (int)maxMarkerWidth);
2635 #else
2636             rx = (int)MAX(maxMarkerWidth, pointSize);
2637 #endif
2638
2639             // The bullet text will be right aligned at the first tab marker, followed
2640             // by a space, followed by the list item text.  The space is arbitrarily
2641             // picked as pointSize*2/3.  The space on the first line of the text item
2642             // is established by a left aligned tab, on subsequent lines it's established
2643             // by the head indent.
2644             NSMutableParagraphStyle *mps = [[NSMutableParagraphStyle alloc] init];
2645             [mps setFirstLineHeadIndent: 0];
2646             [mps setHeadIndent: rx];
2647             [mps setTabStops:[NSArray arrayWithObjects:
2648                         [[[NSTextTab alloc] initWithType:NSRightTabStopType location:rx-(pointSize*2/3)] autorelease],
2649                         [[[NSTextTab alloc] initWithType:NSLeftTabStopType location:rx] autorelease],
2650                         nil]];
2651             NSRange tempRange = { info.start, info.end-info.start }; // workaround for 4213314
2652             [result addAttribute:NSParagraphStyleAttributeName value:mps range:tempRange];
2653             [mps release];
2654         }
2655     }
2656
2657     return result;
2658
2659     END_BLOCK_OBJC_EXCEPTIONS;
2660
2661     return nil;
2662 }
2663
2664 NSImage *FrameMac::imageFromRect(NSRect rect) const
2665 {
2666     NSView *view = d->m_view->getDocumentView();
2667     if (!view)
2668         return nil;
2669     
2670     NSImage *resultImage;
2671     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2672     
2673     NSRect bounds = [view bounds];
2674     
2675     // Round image rect size in window coordinate space to avoid pixel cracks at HiDPI (4622794)
2676     rect = [view convertRect:rect toView:nil];
2677     rect.size.height = roundf(rect.size.height);
2678     rect.size.width = roundf(rect.size.width);
2679     rect = [view convertRect:rect fromView:nil];
2680     
2681     resultImage = [[[NSImage alloc] initWithSize:rect.size] autorelease];
2682
2683     if (rect.size.width != 0 && rect.size.height != 0) {
2684         [resultImage setFlipped:YES];
2685         [resultImage lockFocus];
2686
2687         CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
2688
2689         CGContextSaveGState(context);
2690         CGContextTranslateCTM(context, bounds.origin.x - rect.origin.x, bounds.origin.y - rect.origin.y);
2691         [view drawRect:rect];
2692         CGContextRestoreGState(context);
2693
2694         [resultImage unlockFocus];
2695         [resultImage setFlipped:NO];
2696     }
2697
2698     return resultImage;
2699
2700     END_BLOCK_OBJC_EXCEPTIONS;
2701     
2702     return nil;
2703 }
2704
2705 NSImage* FrameMac::selectionImage(bool forceWhiteText) const
2706 {
2707     d->m_paintRestriction = forceWhiteText ? PaintRestrictionSelectionOnlyWhiteText : PaintRestrictionSelectionOnly;
2708     NSImage *result = imageFromRect(visibleSelectionRect());
2709     d->m_paintRestriction = PaintRestrictionNone;
2710     return result;
2711 }
2712
2713 NSImage *FrameMac::snapshotDragImage(Node *node, NSRect *imageRect, NSRect *elementRect) const
2714 {
2715     RenderObject *renderer = node->renderer();
2716     if (!renderer)
2717         return nil;
2718     
2719     renderer->updateDragState(true);    // mark dragged nodes (so they pick up the right CSS)
2720     d->m_doc->updateLayout();        // forces style recalc - needed since changing the drag state might
2721                                         // imply new styles, plus JS could have changed other things
2722     IntRect topLevelRect;
2723     NSRect paintingRect = renderer->paintingRootRect(topLevelRect);
2724
2725     d->m_elementToDraw = node;              // invoke special sub-tree drawing mode
2726     NSImage *result = imageFromRect(paintingRect);
2727     renderer->updateDragState(false);
2728     d->m_doc->updateLayout();
2729     d->m_elementToDraw = 0;
2730
2731     if (elementRect)
2732         *elementRect = topLevelRect;
2733     if (imageRect)
2734         *imageRect = paintingRect;
2735     return result;
2736 }
2737
2738 NSFont *FrameMac::fontForSelection(bool *hasMultipleFonts) const
2739 {
2740     if (hasMultipleFonts)
2741         *hasMultipleFonts = false;
2742
2743     if (!selectionController()->isRange()) {
2744         Node *nodeToRemove;
2745         RenderStyle *style = styleForSelectionStart(nodeToRemove); // sets nodeToRemove
2746
2747         NSFont *result = 0;
2748         if (style)
2749             result = style->font().primaryFont()->getNSFont();
2750         
2751         if (nodeToRemove) {
2752             ExceptionCode ec;
2753             nodeToRemove->remove(ec);
2754             ASSERT(ec == 0);
2755         }
2756
2757         return result;
2758     }
2759
2760     NSFont *font = nil;
2761
2762     RefPtr<Range> range = selectionController()->toRange();
2763     Node *startNode = range->editingStartPosition().node();
2764     if (startNode != nil) {
2765         Node *pastEnd = range->pastEndNode();
2766         // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one
2767         // unreproducible case where this didn't happen, so check for nil also.
2768         for (Node *n = startNode; n && n != pastEnd; n = n->traverseNextNode()) {
2769             RenderObject *renderer = n->renderer();
2770             if (!renderer)
2771                 continue;
2772             // FIXME: Are there any node types that have renderers, but that we should be skipping?
2773             NSFont *f = renderer->style()->font().primaryFont()->getNSFont();
2774             if (!font) {
2775                 font = f;
2776                 if (!hasMultipleFonts)
2777                     break;
2778             } else if (font != f) {
2779                 *hasMultipleFonts = true;
2780                 break;
2781             }
2782         }
2783     }
2784
2785     return font;
2786 }
2787
2788 NSDictionary *FrameMac::fontAttributesForSelectionStart() const
2789 {
2790     Node *nodeToRemove;
2791     RenderStyle *style = styleForSelectionStart(nodeToRemove);
2792     if (!style)
2793         return nil;
2794
2795     NSMutableDictionary *result = [NSMutableDictionary dictionary];
2796
2797     if (style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0)
2798         [result setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2799
2800     if (style->font().primaryFont()->getNSFont())
2801         [result setObject:style->font().primaryFont()->getNSFont() forKey:NSFontAttributeName];
2802
2803     if (style->color().isValid() && style->color() != Color::black)
2804         [result setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2805
2806     ShadowData *shadow = style->textShadow();
2807     if (shadow) {
2808         NSShadow *s = [[NSShadow alloc] init];
2809         [s setShadowOffset:NSMakeSize(shadow->x, shadow->y)];
2810         [s setShadowBlurRadius:shadow->blur];
2811         [s setShadowColor:nsColor(shadow->color)];
2812         [result setObject:s forKey:NSShadowAttributeName];
2813     }
2814
2815     int decoration = style->textDecorationsInEffect();
2816     if (decoration & LINE_THROUGH)
2817         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSStrikethroughStyleAttributeName];
2818
2819     int superscriptInt = 0;
2820     switch (style->verticalAlign()) {
2821         case BASELINE:
2822         case BOTTOM:
2823         case BASELINE_MIDDLE:
2824         case LENGTH:
2825         case MIDDLE:
2826         case TEXT_BOTTOM:
2827         case TEXT_TOP:
2828         case TOP:
2829             break;
2830         case SUB:
2831             superscriptInt = -1;
2832             break;
2833         case SUPER:
2834             superscriptInt = 1;
2835             break;
2836     }
2837     if (superscriptInt)
2838         [result setObject:[NSNumber numberWithInt:superscriptInt] forKey:NSSuperscriptAttributeName];
2839
2840     if (decoration & UNDERLINE)
2841         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
2842
2843     if (nodeToRemove) {
2844         ExceptionCode ec = 0;
2845         nodeToRemove->remove(ec);
2846         ASSERT(ec == 0);
2847     }
2848
2849     return result;
2850 }
2851
2852 NSWritingDirection FrameMac::baseWritingDirectionForSelectionStart() const
2853 {
2854     NSWritingDirection result = NSWritingDirectionLeftToRight;
2855
2856     Position pos = selectionController()->selection().visibleStart().deepEquivalent();
2857     Node *node = pos.node();
2858     if (!node || !node->renderer() || !node->renderer()->containingBlock())
2859         return result;
2860     RenderStyle *style = node->renderer()->containingBlock()->style();
2861     if (!style)
2862         return result;
2863         
2864     switch (style->direction()) {
2865         case LTR:
2866             result = NSWritingDirectionLeftToRight;
2867             break;
2868         case RTL:
2869             result = NSWritingDirectionRightToLeft;
2870             break;
2871     }
2872
2873     return result;
2874 }
2875
2876 void FrameMac::tokenizerProcessedData()
2877 {
2878     if (d->m_doc)
2879         checkCompleted();
2880     [_bridge tokenizerProcessedData];
2881 }
2882
2883 void FrameMac::setBridge(WebCoreFrameBridge *bridge)
2884
2885     if (_bridge == bridge)
2886         return;
2887
2888     HardRetain(bridge);
2889     HardRelease(_bridge);
2890     _bridge = bridge;
2891 }
2892
2893 String FrameMac::overrideMediaType() const
2894 {
2895     NSString *overrideType = [_bridge overrideMediaType];
2896     if (overrideType)
2897         return overrideType;
2898     return String();
2899 }
2900
2901 NSColor *FrameMac::bodyBackgroundColor() const
2902 {
2903     if (document() && document()->body() && document()->body()->renderer()) {
2904         Color bgColor = document()->body()->renderer()->style()->backgroundColor();
2905         if (bgColor.isValid())
2906             return nsColor(bgColor);
2907     }
2908     return nil;
2909 }
2910
2911 WebCoreKeyboardUIMode FrameMac::keyboardUIMode() const
2912 {
2913     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2914     return [_bridge keyboardUIMode];
2915     END_BLOCK_OBJC_EXCEPTIONS;
2916
2917     return WebCoreKeyboardAccessDefault;
2918 }
2919
2920 void FrameMac::didTellBridgeAboutLoad(const String& URL)
2921 {
2922     urlsBridgeKnowsAbout.add(URL);
2923 }
2924
2925 bool FrameMac::haveToldBridgeAboutLoad(const String& URL)
2926 {
2927     return urlsBridgeKnowsAbout.contains(URL);
2928 }
2929
2930 void FrameMac::clear()
2931 {
2932     urlsBridgeKnowsAbout.clear();
2933     setMarkedTextRange(0, nil, nil);
2934     Frame::clear();
2935 }
2936
2937 void FrameMac::print()
2938 {
2939     [Mac(this)->_bridge print];
2940 }
2941
2942 KJS::Bindings::Instance *FrameMac::getAppletInstanceForWidget(Widget *widget)
2943 {
2944     NSView *aView = widget->getView();
2945     if (!aView)
2946         return 0;
2947     jobject applet;
2948     
2949     // Get a pointer to the actual Java applet instance.
2950     if ([_bridge respondsToSelector:@selector(getAppletInView:)])
2951         applet = [_bridge getAppletInView:aView];
2952     else
2953         applet = [_bridge pollForAppletInView:aView];
2954     
2955     if (applet) {
2956         // Wrap the Java instance in a language neutral binding and hand
2957         // off ownership to the APPLET element.
2958         KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2959         KJS::Bindings::Instance *instance = KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::JavaLanguage, applet, executionContext);        
2960         return instance;
2961     }
2962     
2963     return 0;
2964 }
2965
2966 static KJS::Bindings::Instance *getInstanceForView(NSView *aView)
2967 {
2968     if ([aView respondsToSelector:@selector(objectForWebScript)]){
2969         id object = [aView objectForWebScript];
2970         if (object) {
2971             KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2972             return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::ObjectiveCLanguage, object, executionContext);
2973         }
2974     }
2975     else if ([aView respondsToSelector:@selector(createPluginScriptableObject)]) {
2976         NPObject *object = [aView createPluginScriptableObject];
2977         if (object) {
2978             KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction()(aView);
2979             KJS::Bindings::Instance *instance = KJS::Bindings::Instance::createBindingForLanguageInstance(KJS::Bindings::Instance::CLanguage, object, executionContext);
2980             
2981             // -createPluginScriptableObject returns a retained NPObject.  The caller is expected to release it.
2982             _NPN_ReleaseObject(object);
2983             
2984             return instance;
2985         }
2986     }
2987     return 0;
2988 }
2989
2990 KJS::Bindings::Instance *FrameMac::getEmbedInstanceForWidget(Widget *widget)
2991 {
2992     return getInstanceForView(widget->getView());
2993 }
2994
2995 KJS::Bindings::Instance *FrameMac::getObjectInstanceForWidget(Widget *widget)
2996 {
2997     return getInstanceForView(widget->getView());
2998 }
2999
3000 void FrameMac::addPluginRootObject(KJS::Bindings::RootObject *root)
3001 {
3002     m_rootObjects.append(root);
3003 }
3004
3005 void FrameMac::cleanupPluginObjects()
3006 {
3007     // Delete old plug-in data structures
3008     JSLock lock;
3009     
3010     unsigned count = m_rootObjects.size();
3011     for (unsigned i = 0; i < count; i++)
3012         m_rootObjects[i]->removeAllNativeReferences();
3013     m_rootObjects.clear();
3014     
3015     _bindingRoot = 0;
3016     HardRelease(_windowScriptObject);
3017     _windowScriptObject = 0;
3018     
3019     if (_windowScriptNPObject) {
3020         // Call _NPN_DeallocateObject() instead of _NPN_ReleaseObject() so that we don't leak if a plugin fails to release the window
3021         // script object properly.
3022         // This shouldn't cause any problems for plugins since they should have already been stopped and destroyed at this point.
3023         _NPN_DeallocateObject(_windowScriptNPObject);
3024         _windowScriptNPObject = 0;
3025     }
3026 }
3027
3028 void FrameMac::registerCommandForUndoOrRedo(PassRefPtr<EditCommand> cmd, bool isRedo)
3029 {
3030     ASSERT(cmd);
3031     WebUndoAction action = static_cast<WebUndoAction>(cmd->editingAction());
3032     NSUndoManager* undoManager = [_bridge undoManager];
3033     WebCoreEditCommand* command = [WebCoreEditCommand commandWithEditCommand:cmd];
3034     NSString* actionName = [_bridge nameForUndoAction:action];
3035     [undoManager registerUndoWithTarget:_bridge selector:(isRedo ? @selector(redoEditing:) : @selector(undoEditing:)) object:command];
3036     if (actionName)
3037         [undoManager setActionName:actionName];
3038     _haveUndoRedoOperations = YES;
3039 }
3040
3041 void FrameMac::registerCommandForUndo(PassRefPtr<EditCommand> cmd)
3042 {
3043     registerCommandForUndoOrRedo(cmd, false);
3044 }
3045
3046 void FrameMac::registerCommandForRedo(PassRefPtr<EditCommand> cmd)
3047 {
3048     registerCommandForUndoOrRedo(cmd, true);
3049 }
3050
3051 void FrameMac::clearUndoRedoOperations()
3052 {
3053     if (_haveUndoRedoOperations) {
3054         // workaround for <rdar://problem/4645507> NSUndoManager dies
3055         // with uncaught exception when undo items cleared while
3056         // groups are open
3057         NSUndoManager *undoManager = [_bridge undoManager];
3058         int groupingLevel = [undoManager groupingLevel];
3059         for (int i = 0; i < groupingLevel; ++i)
3060             [undoManager endUndoGrouping];
3061         
3062         [undoManager removeAllActionsWithTarget:_bridge];
3063
3064         for (int i = 0; i < groupingLevel; ++i)
3065             [undoManager beginUndoGrouping];
3066
3067         _haveUndoRedoOperations = NO;
3068     }
3069 }
3070
3071 void FrameMac::issueUndoCommand()
3072 {
3073     if (canUndo())
3074         [[_bridge undoManager] undo];
3075 }
3076
3077 void FrameMac::issueRedoCommand()
3078 {
3079     if (canRedo())
3080         [[_bridge undoManager] redo];
3081 }
3082
3083 void FrameMac::issueCutCommand()
3084 {
3085     [_bridge issueCutCommand];
3086 }
3087
3088 void FrameMac::issueCopyCommand()
3089 {
3090     [_bridge issueCopyCommand];
3091 }
3092
3093 void FrameMac::issuePasteCommand()
3094 {
3095     [_bridge issuePasteCommand];
3096 }
3097
3098 void FrameMac::issuePasteAndMatchStyleCommand()
3099 {
3100     [_bridge issuePasteAndMatchStyleCommand];
3101 }
3102
3103 void FrameMac::issueTransposeCommand()
3104 {
3105     [_bridge issueTransposeCommand];
3106 }
3107
3108 bool FrameMac::canUndo() const
3109 {
3110     return [[Mac(this)->_bridge undoManager] canUndo];
3111 }
3112
3113 bool FrameMac::canRedo() const
3114 {
3115     return [[Mac(this)->_bridge undoManager] canRedo];
3116 }
3117
3118 bool FrameMac::canPaste() const
3119 {
3120     return [Mac(this)->_bridge canPaste];
3121 }
3122
3123 void FrameMac::markMisspellingsInAdjacentWords(const VisiblePosition &p)
3124 {
3125     if (![_bridge isContinuousSpellCheckingEnabled])
3126         return;
3127     markMisspellings(Selection(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)));
3128 }
3129
3130 void FrameMac::markMisspellings(const Selection& selection)
3131 {
3132     // This function is called with a selection already expanded to word boundaries.
3133     // Might be nice to assert that here.
3134
3135     if (![_bridge isContinuousSpellCheckingEnabled])
3136         return;
3137
3138     RefPtr<Range> searchRange(selection.toRange());
3139     if (!searchRange || searchRange->isDetached())
3140         return;
3141     
3142     // If we're not in an editable node, bail.
3143     int exception = 0;
3144     Node *editableNode = searchRange->startContainer(exception);
3145     if (!editableNode->isContentEditable())
3146         return;
3147     
3148     // Get the spell checker if it is available
3149     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
3150     if (checker == nil)
3151         return;
3152     
3153     WordAwareIterator it(searchRange.get());
3154     
3155     while (!it.atEnd()) {      // we may be starting at the end of the doc, and already by atEnd
3156         const UChar* chars = it.characters();
3157         int len = it.length();
3158         if (len > 1 || !DeprecatedChar(chars[0]).isSpace()) {
3159             NSString *chunk = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(chars) length:len freeWhenDone:NO];
3160             int startIndex = 0;
3161             // Loop over the chunk to find each misspelling in it.
3162             while (startIndex < len) {
3163                 NSRange misspelling = [checker checkSpellingOfString:chunk startingAt:startIndex language:nil wrap:NO inSpellDocumentWithTag:[_bridge spellCheckerDocumentTag] wordCount:NULL];
3164                 if (misspelling.length == 0)
3165                     break;
3166                 else {
3167                     // Build up result range and string.  Note the misspelling may span many text nodes,
3168                     // but the CharIterator insulates us from this complexity
3169                     RefPtr<Range> misspellingRange(rangeOfContents(document()));
3170                     CharacterIterator chars(it.range().get());
3171                     chars.advance(misspelling.location);
3172                     misspellingRange->setStart(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
3173                     chars.advance(misspelling.length);
3174                     misspellingRange->setEnd(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
3175                     // Mark misspelling in document.
3176                     document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
3177                     startIndex = misspelling.location + misspelling.length;
3178                 }
3179             }
3180             [chunk release];
3181         }
3182     
3183         it.advance();
3184     }
3185 }
3186
3187 void FrameMac::respondToChangedSelection(const Selection &oldSelection, bool closeTyping)
3188 {
3189     if (document()) {
3190         if ([_bridge isContinuousSpellCheckingEnabled]) {
3191             Selection oldAdjacentWords;
3192             
3193             // If this is a change in selection resulting from a delete operation, oldSelection may no longer
3194             // be in the document.
3195             if (oldSelection.start().node() && oldSelection.start().node()->inDocument()) {
3196                 VisiblePosition oldStart(oldSelection.visibleStart());
3197                 oldAdjacentWords = Selection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));   
3198             }
3199
3200             VisiblePosition newStart(selectionController()->selection().visibleStart());
3201             Selection newAdjacentWords(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
3202
3203             // When typing we check spelling elsewhere, so don't redo it here.
3204             if (closeTyping && oldAdjacentWords != newAdjacentWords)
3205                 markMisspellings(oldAdjacentWords);
3206
3207             // This only erases a marker in the first word of the selection.
3208             // Perhaps peculiar, but it matches AppKit.
3209             document()->removeMarkers(newAdjacentWords.toRange().get(), DocumentMarker::Spelling);
3210         } else
3211             // When continuous spell checking is off, no markers appear after the selection changes.
3212             document()->removeMarkers(DocumentMarker::Spelling);
3213     }
3214
3215     [_bridge respondToChangedSelection];
3216 }
3217
3218 bool FrameMac::shouldChangeSelection(const Selection& oldSelection, const Selection& newSelection, EAffinity affinity, bool stillSelecting) const
3219 {
3220     return [_bridge shouldChangeSelectedDOMRange:[DOMRange _rangeWith:oldSelection.toRange().get()]
3221                                       toDOMRange:[DOMRange _rangeWith:newSelection.toRange().get()]
3222                                         affinity:static_cast<NSSelectionAffinity>(affinity)
3223                                   stillSelecting:stillSelecting];
3224 }
3225
3226 bool FrameMac::shouldDeleteSelection(const Selection& selection) const
3227 {
3228     return [_bridge shouldDeleteSelectedDOMRange:[DOMRange _rangeWith:selection.toRange().get()]];
3229 }
3230
3231 void FrameMac::respondToChangedContents(const Selection& selection)
3232 {
3233     if (AXObjectCache::accessibilityEnabled()) {
3234         Node* node = selection.start().node();
3235         if (node)
3236             renderer()->document()->axObjectCache()->postNotification(node->renderer(), "AXValueChanged");
3237     }
3238     [_bridge respondToChangedContents];
3239 }
3240
3241 bool FrameMac::isContentEditable() const
3242 {
3243     return Frame::isContentEditable() || [_bridge isEditable];
3244 }
3245
3246 bool FrameMac::shouldBeginEditing(const Range *range) const
3247 {
3248     ASSERT(range);
3249     return [_bridge shouldBeginEditing:[DOMRange _rangeWith:const_cast<Range*>(range)]];
3250 }
3251
3252 bool FrameMac::shouldEndEditing(const Range *range) const
3253 {
3254     ASSERT(range);
3255     return [_bridge shouldEndEditing:[DOMRange _rangeWith:const_cast<Range*>(range)]];
3256 }
3257
3258 void FrameMac::didBeginEditing() const
3259 {
3260     [_bridge didBeginEditing];
3261 }
3262
3263 void FrameMac::didEndEditing() const
3264 {
3265     [_bridge didEndEditing];
3266 }
3267
3268 void FrameMac::textFieldDidBeginEditing(Element* input)
3269 {
3270     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3271     [_bridge textFieldDidBeginEditing:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
3272     END_BLOCK_OBJC_EXCEPTIONS;
3273 }
3274
3275 void FrameMac::textFieldDidEndEditing(Element* input)
3276 {
3277     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3278     [_bridge textFieldDidEndEditing:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
3279     END_BLOCK_OBJC_EXCEPTIONS;
3280 }
3281
3282 void FrameMac::textDidChangeInTextField(Element* input)
3283 {
3284     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3285     [_bridge textDidChangeInTextField:(DOMHTMLInputElement *)[DOMElement _elementWith:input]];
3286     END_BLOCK_OBJC_EXCEPTIONS;
3287 }
3288
3289 void FrameMac::textDidChangeInTextArea(Element* textarea)
3290 {
3291     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3292     [_bridge textDidChangeInTextArea:(DOMHTMLTextAreaElement *)[DOMElement _elementWith:textarea]];
3293     END_BLOCK_OBJC_EXCEPTIONS;
3294 }
3295
3296 bool FrameMac::doTextFieldCommandFromEvent(Element* input, const PlatformKeyboardEvent* event)
3297 {
3298     // FIXME: We might eventually need to make sure key bindings for editing work even with
3299     // events created with the DOM API. Those don't have a PlatformKeyboardEvent.
3300     if (!event)
3301         return false;
3302
3303     BOOL result = false;
3304     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3305     SEL selector = selectorForKeyEvent(event);
3306     if (selector)
3307         result = [_bridge textField:(DOMHTMLInputElement *)[DOMElement _elementWith:input] doCommandBySelector:selector];
3308     END_BLOCK_OBJC_EXCEPTIONS;
3309     return result;
3310 }
3311
3312 void FrameMac::textWillBeDeletedInTextField(Element* input)
3313 {
3314     // We're using the deleteBackward selector for all deletion operations since the autofill code treats all deletions the same way.
3315     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3316     [_bridge textField:(DOMHTMLInputElement *)[DOMElement _elementWith:input] doCommandBySelector:@selector(deleteBackward:)];
3317     END_BLOCK_OBJC_EXCEPTIONS;
3318 }
3319
3320 bool FrameMac::inputManagerHasMarkedText() const
3321 {
3322     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3323     return [[NSInputManager currentInputManager] hasMarkedText];
3324     END_BLOCK_OBJC_EXCEPTIONS
3325     return false;
3326 }
3327
3328 const short enableRomanKeyboardsOnly = -23;
3329 void FrameMac::setSecureKeyboardEntry(bool enable)
3330 {
3331     if (enable) {
3332         EnableSecureEventInput();
3333 // FIXME: KeyScript is deprecated in Leopard, we need a new solution for this <rdar://problem/4727607>
3334 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
3335         KeyScript(enableRomanKeyboardsOnly);
3336 #endif
3337     } else {
3338         DisableSecureEventInput();
3339 #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4
3340         KeyScript(smKeyEnableKybds);
3341 #endif
3342     }
3343 }
3344
3345 bool FrameMac::isSecureKeyboardEntry()
3346 {
3347     return IsSecureEventInputEnabled();
3348 }
3349
3350 static void convertAttributesToUnderlines(Vector<MarkedTextUnderline>& result, const Range *markedTextRange, NSArray *attributes, NSArray *ranges)
3351 {
3352     int exception = 0;
3353     int baseOffset = markedTextRange->startOffset(exception);
3354
3355     unsigned length = [attributes count];
3356     ASSERT([ranges count] == length);
3357
3358     for (unsigned i = 0; i < length; i++) {
3359         NSNumber *style = [[attributes objectAtIndex:i] objectForKey:NSUnderlineStyleAttributeName];
3360         if (!style)
3361             continue;
3362         NSRange range = [[ranges objectAtIndex:i] rangeValue];
3363         NSColor *color = [[attributes objectAtIndex:i] objectForKey:NSUnderlineColorAttributeName];
3364         Color qColor = Color::black;
3365         if (color) {
3366             NSColor* deviceColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
3367             qColor = Color(makeRGBA((int)(255 * [deviceColor redComponent]),
3368                                     (int)(255 * [deviceColor blueComponent]),
3369                                     (int)(255 * [deviceColor greenComponent]),
3370                                     (int)(255 * [deviceColor alphaComponent])));
3371         }
3372
3373         result.append(MarkedTextUnderline(range.location + baseOffset, 
3374                                           range.location + baseOffset + range.length, 
3375                                           qColor,
3376                                           [style intValue] > 1));
3377     }
3378 }
3379
3380 void FrameMac::setMarkedTextRange(const Range *range, NSArray *attributes, NSArray *ranges)
3381 {
3382     int exception = 0;
3383
3384     ASSERT(!range || range->startContainer(exception) == range->endContainer(exception));
3385     ASSERT(!range || range->collapsed(exception) || range->startContainer(exception)->isTextNode());
3386
3387     d->m_markedTextUnderlines.clear();
3388     if (attributes == nil)
3389         d->m_markedTextUsesUnderlines = false;
3390     else {
3391         d->m_markedTextUsesUnderlines = true;
3392         convertAttributesToUnderlines(d->m_markedTextUnderlines, range, attributes, ranges);
3393     }
3394
3395     if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
3396         m_markedTextRange->startContainer(exception)->renderer()->repaint();
3397
3398     if (range && range->collapsed(exception))
3399         m_markedTextRange = 0;
3400     else
3401         m_markedTextRange = const_cast<Range*>(range);
3402
3403     if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
3404         m_markedTextRange->startContainer(exception)->renderer()->repaint();
3405 }
3406
3407 bool FrameMac::canGoBackOrForward(int distance) const
3408 {
3409     return [_bridge canGoBackOrForward:distance];
3410 }
3411
3412 void FrameMac::didFirstLayout()
3413 {
3414     [_bridge didFirstLayout];
3415 }
3416
3417 NSMutableDictionary *FrameMac::dashboardRegionsDictionary()
3418 {
3419     Document *doc = document();
3420     if (!doc)
3421         return nil;
3422
3423     const Vector<DashboardRegionValue>& regions = doc->dashboardRegions();
3424     size_t n = regions.size();
3425
3426     // Convert the DeprecatedValueList<DashboardRegionValue> into a NSDictionary of WebDashboardRegions
3427     NSMutableDictionary *webRegions = [NSMutableDictionary dictionaryWithCapacity:n];
3428     for (size_t i = 0; i < n; i++) {
3429         const DashboardRegionValue& region = regions[i];
3430
3431         if (region.type == StyleDashboardRegion::None)
3432             continue;
3433         
3434         NSString *label = region.label;
3435         WebDashboardRegionType type = WebDashboardRegionTypeNone;
3436         if (region.type == StyleDashboardRegion::Circle)
3437             type = WebDashboardRegionTypeCircle;
3438         else if (region.type == StyleDashboardRegion::Rectangle)
3439             type = WebDashboardRegionTypeRectangle;
3440         NSMutableArray *regionValues = [webRegions objectForKey:label];
3441         if (!regionValues) {
3442             regionValues = [[NSMutableArray alloc] initWithCapacity:1];
3443             [webRegions setObject:regionValues forKey:label];
3444             [regionValues release];
3445         }
3446         
3447         WebDashboardRegion *webRegion = [[WebDashboardRegion alloc] initWithRect:region.bounds clip:region.clip type:type];
3448         [regionValues addObject:webRegion];
3449         [webRegion release];
3450     }
3451     
3452     return webRegions;
3453 }
3454
3455 void FrameMac::dashboardRegionsChanged()
3456 {
3457     NSMutableDictionary *webRegions = dashboardRegionsDictionary();
3458     [_bridge dashboardRegionsChanged:webRegions];
3459 }
3460
3461 void FrameMac::willPopupMenu(NSMenu * menu)
3462 {
3463     [_bridge willPopupMenu:menu];
3464 }
3465
3466 bool FrameMac::isCharacterSmartReplaceExempt(UChar c, bool isPreviousChar)
3467 {
3468     return [_bridge isCharacterSmartReplaceExempt:c isPreviousCharacter:isPreviousChar];
3469 }
3470
3471 void FrameMac::handledOnloadEvents()
3472 {
3473     [_bridge handledOnloadEvents];
3474 }
3475
3476 bool FrameMac::shouldClose()
3477 {
3478     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3479
3480     if (![_bridge canRunBeforeUnloadConfirmPanel])
3481         return true;
3482
3483     RefPtr<Document> doc = document();
3484     if (!doc)
3485         return true;
3486     HTMLElement* body = doc->body();
3487     if (!body)
3488         return true;
3489
3490     RefPtr<BeforeUnloadEvent> event = new BeforeUnloadEvent;
3491     event->setTarget(doc.get());
3492     doc->handleWindowEvent(event.get(), false);
3493
3494     if (!event->defaultPrevented() && doc)
3495         doc->defaultEventHandler(event.get());
3496     if (event->result().isNull())
3497         return true;
3498
3499     String text = event->result();
3500     text.replace('\\', backslashAsCurrencySymbol());
3501
3502     return [_bridge runBeforeUnloadConfirmPanelWithMessage:text];
3503
3504     END_BLOCK_OBJC_EXCEPTIONS;
3505
3506     return true;
3507 }
3508
3509 void FrameMac::dragSourceMovedTo(const PlatformMouseEvent& event)
3510 {
3511     if (_dragSrc && _dragSrcMayBeDHTML)
3512         // for now we don't care if event handler cancels default behavior, since there is none
3513         dispatchDragSrcEvent(dragEvent, event);
3514 }
3515
3516 void FrameMac::dragSourceEndedAt(const PlatformMouseEvent& event, NSDragOperation operation)
3517 {
3518     if (_dragSrc && _dragSrcMayBeDHTML) {
3519         _dragClipboard->setDestinationOperation(operation);
3520         // for now we don't care if event handler cancels default behavior, since there is none
3521         dispatchDragSrcEvent(dragendEvent, event);
3522     }
3523     freeClipboard();
3524     _dragSrc = 0;
3525 }
3526
3527 // returns if we should continue "default processing", i.e., whether eventhandler canceled
3528 bool FrameMac::dispatchDragSrcEvent(const AtomicString &eventType, const PlatformMouseEvent& event) const
3529 {
3530     bool noDefaultProc = d->m_view->dispatchDragEvent(eventType, _dragSrc.get(), event, _dragClipboard.get());
3531     return !noDefaultProc;
3532 }
3533
3534 void Frame::setNeedsReapplyStyles()
3535 {
3536     [Mac(this)->bridge() setNeedsReapplyStyles];
3537 }
3538
3539 FloatRect FrameMac::customHighlightLineRect(const AtomicString& type, const FloatRect& lineRect)
3540 {
3541     return [bridge() customHighlightRect:type forLine:lineRect];
3542 }
3543
3544 void FrameMac::paintCustomHighlight(const AtomicString& type, const FloatRect& boxRect, const FloatRect& lineRect, bool text, bool line)
3545 {
3546     [bridge() paintCustomHighlight:type forBox:boxRect onLine:lineRect behindText:text entireLine:line];
3547 }
3548
3549 KURL FrameMac::originalRequestURL() const
3550 {
3551     return [_bridge originalRequestURL];
3552 }
3553
3554 bool FrameMac::isLoadTypeReload()
3555 {
3556     return [_bridge isLoadTypeReload];
3557 }
3558
3559 }