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