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