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