2006-03-17 Eric Seidel <eseidel@apple.com>
[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)
1835 {
1836     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1837
1838     switch ([_currentEvent type]) {
1839         case NSMouseMoved: {
1840             Node *node = event.innerNode();
1841             if (!node)
1842                 return false;
1843             RenderObject *renderer = node->renderer();
1844             if (!renderer || !renderer->isWidget())
1845                 return false;
1846             Widget *widget = static_cast<RenderWidget *>(renderer)->widget();
1847             if (!widget || !widget->isFrameView())
1848                 return false;
1849             Frame *subframePart = static_cast<FrameView *>(widget)->frame();
1850             if (!subframePart)
1851                 return false;
1852             [Mac(subframePart)->bridge() mouseMoved:_currentEvent];
1853             return true;
1854         }
1855         
1856         case NSLeftMouseDown: {
1857             Node *node = event.innerNode();
1858             if (!node) {
1859                 return false;
1860             }
1861             RenderObject *renderer = node->renderer();
1862             if (!renderer || !renderer->isWidget()) {
1863                 return false;
1864             }
1865             Widget *widget = static_cast<RenderWidget *>(renderer)->widget();
1866             if (!widget || !widget->isFrameView())
1867                 return false;
1868             if (!passWidgetMouseDownEventToWidget(static_cast<RenderWidget *>(renderer))) {
1869                 return false;
1870             }
1871             _mouseDownWasInSubframe = true;
1872             return true;
1873         }
1874         case NSLeftMouseUp: {
1875             if (!_mouseDownWasInSubframe) {
1876                 return false;
1877             }
1878             NSView *view = mouseDownViewIfStillGood();
1879             if (!view) {
1880                 return false;
1881             }
1882             ASSERT(!_sendingEventToSubview);
1883             _sendingEventToSubview = true;
1884             [view mouseUp:_currentEvent];
1885             _sendingEventToSubview = false;
1886             return true;
1887         }
1888         case NSLeftMouseDragged: {
1889             if (!_mouseDownWasInSubframe) {
1890                 return false;
1891             }
1892             NSView *view = mouseDownViewIfStillGood();
1893             if (!view) {
1894                 return false;
1895             }
1896             ASSERT(!_sendingEventToSubview);
1897             _sendingEventToSubview = true;
1898             [view mouseDragged:_currentEvent];
1899             _sendingEventToSubview = false;
1900             return true;
1901         }
1902         default:
1903             return false;
1904     }
1905     END_BLOCK_OBJC_EXCEPTIONS;
1906
1907     return false;
1908 }
1909
1910 bool FrameMac::passWheelEventToChildWidget(Node *node)
1911 {
1912     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1913         
1914     if ([_currentEvent type] != NSScrollWheel || _sendingEventToSubview || !node) 
1915         return false;
1916     else {
1917         RenderObject *renderer = node->renderer();
1918         if (!renderer || !renderer->isWidget())
1919             return false;
1920         Widget *widget = static_cast<RenderWidget *>(renderer)->widget();
1921         if (!widget)
1922             return false;
1923             
1924         NSView *nodeView = widget->getView();
1925         ASSERT(nodeView);
1926         ASSERT([nodeView superview]);
1927         NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[_currentEvent locationInWindow] fromView:nil]];
1928     
1929         ASSERT(view);
1930         _sendingEventToSubview = true;
1931         [view scrollWheel:_currentEvent];
1932         _sendingEventToSubview = false;
1933         return true;
1934     }
1935             
1936     END_BLOCK_OBJC_EXCEPTIONS;
1937     return false;
1938 }
1939
1940 void FrameMac::mouseDown(NSEvent *event)
1941 {
1942     FrameView *v = d->m_view.get();
1943     if (!v || _sendingEventToSubview)
1944         return;
1945
1946     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1947
1948     prepareForUserAction();
1949
1950     _mouseDownView = nil;
1951     _dragSrc = 0;
1952     
1953     NSEvent *oldCurrentEvent = _currentEvent;
1954     _currentEvent = KWQRetain(event);
1955     m_mouseDown = PlatformMouseEvent(event);
1956     NSPoint loc = [event locationInWindow];
1957     d->m_view->viewportToContents((int)loc.x, (int)loc.y, _mouseDownX, _mouseDownY);
1958     _mouseDownTimestamp = [event timestamp];
1959
1960     _mouseDownMayStartDrag = false;
1961     _mouseDownMayStartSelect = false;
1962
1963     v->handleMousePressEvent(event);
1964     
1965     ASSERT(_currentEvent == event);
1966     KWQRelease(event);
1967     _currentEvent = oldCurrentEvent;
1968
1969     END_BLOCK_OBJC_EXCEPTIONS;
1970 }
1971
1972 void FrameMac::mouseDragged(NSEvent *event)
1973 {
1974     FrameView *v = d->m_view.get();
1975     if (!v || _sendingEventToSubview) {
1976         return;
1977     }
1978
1979     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1980
1981     NSEvent *oldCurrentEvent = _currentEvent;
1982     _currentEvent = KWQRetain(event);
1983
1984     v->handleMouseMoveEvent(event);
1985     
1986     ASSERT(_currentEvent == event);
1987     KWQRelease(event);
1988     _currentEvent = oldCurrentEvent;
1989
1990     END_BLOCK_OBJC_EXCEPTIONS;
1991 }
1992
1993 void FrameMac::mouseUp(NSEvent *event)
1994 {
1995     FrameView *v = d->m_view.get();
1996     if (!v || _sendingEventToSubview)
1997         return;
1998
1999     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2000
2001     NSEvent *oldCurrentEvent = _currentEvent;
2002     _currentEvent = KWQRetain(event);
2003
2004     // Our behavior here is a little different that Qt. Qt always sends
2005     // a mouse release event, even for a double click. To correct problems
2006     // in khtml's DOM click event handling we do not send a release here
2007     // for a double click. Instead we send that event from FrameView's
2008     // handleMouseDoubleClickEvent. Note also that the third click of
2009     // a triple click is treated as a single click, but the fourth is then
2010     // treated as another double click. Hence the "% 2" below.
2011     int clickCount = [event clickCount];
2012     if (clickCount > 0 && clickCount % 2 == 0)
2013         v->handleMouseDoubleClickEvent(event);
2014     else
2015         v->handleMouseReleaseEvent(event);
2016     
2017     ASSERT(_currentEvent == event);
2018     KWQRelease(event);
2019     _currentEvent = oldCurrentEvent;
2020     
2021     _mouseDownView = nil;
2022
2023     END_BLOCK_OBJC_EXCEPTIONS;
2024 }
2025
2026 /*
2027  A hack for the benefit of AK's PopUpButton, which uses the Carbon menu manager, which thus
2028  eats all subsequent events after it is starts its modal tracking loop.  After the interaction
2029  is done, this routine is used to fix things up.  When a mouse down started us tracking in
2030  the widget, we post a fake mouse up to balance the mouse down we started with. When a 
2031  key down started us tracking in the widget, we post a fake key up to balance things out.
2032  In addition, we post a fake mouseMoved to get the cursor in sync with whatever we happen to 
2033  be over after the tracking is done.
2034  */
2035 void FrameMac::sendFakeEventsAfterWidgetTracking(NSEvent *initiatingEvent)
2036 {
2037     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2038
2039     _sendingEventToSubview = false;
2040     int eventType = [initiatingEvent type];
2041     if (eventType == NSLeftMouseDown || eventType == NSKeyDown) {
2042         NSEvent *fakeEvent = nil;
2043         if (eventType == NSLeftMouseDown) {
2044             fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
2045                                     location:[initiatingEvent locationInWindow]
2046                                 modifierFlags:[initiatingEvent modifierFlags]
2047                                     timestamp:[initiatingEvent timestamp]
2048                                 windowNumber:[initiatingEvent windowNumber]
2049                                         context:[initiatingEvent context]
2050                                     eventNumber:[initiatingEvent eventNumber]
2051                                     clickCount:[initiatingEvent clickCount]
2052                                     pressure:[initiatingEvent pressure]];
2053         
2054             mouseUp(fakeEvent);
2055         }
2056         else { // eventType == NSKeyDown
2057             fakeEvent = [NSEvent keyEventWithType:NSKeyUp
2058                                     location:[initiatingEvent locationInWindow]
2059                                modifierFlags:[initiatingEvent modifierFlags]
2060                                    timestamp:[initiatingEvent timestamp]
2061                                 windowNumber:[initiatingEvent windowNumber]
2062                                      context:[initiatingEvent context]
2063                                   characters:[initiatingEvent characters] 
2064                  charactersIgnoringModifiers:[initiatingEvent charactersIgnoringModifiers] 
2065                                    isARepeat:[initiatingEvent isARepeat] 
2066                                      keyCode:[initiatingEvent keyCode]];
2067             keyEvent(fakeEvent);
2068         }
2069         // FIXME:  We should really get the current modifierFlags here, but there's no way to poll
2070         // them in Cocoa, and because the event stream was stolen by the Carbon menu code we have
2071         // no up-to-date cache of them anywhere.
2072         fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
2073                                        location:[[_bridge window] convertScreenToBase:[NSEvent mouseLocation]]
2074                                   modifierFlags:[initiatingEvent modifierFlags]
2075                                       timestamp:[initiatingEvent timestamp]
2076                                    windowNumber:[initiatingEvent windowNumber]
2077                                         context:[initiatingEvent context]
2078                                     eventNumber:0
2079                                      clickCount:0
2080                                        pressure:0];
2081         mouseMoved(fakeEvent);
2082     }
2083     
2084     END_BLOCK_OBJC_EXCEPTIONS;
2085 }
2086
2087 void FrameMac::mouseMoved(NSEvent *event)
2088 {
2089     FrameView *v = d->m_view.get();
2090     // Reject a mouse moved if the button is down - screws up tracking during autoscroll
2091     // These happen because WebKit sometimes has to fake up moved events.
2092     if (!v || d->m_bMousePressed || _sendingEventToSubview)
2093         return;
2094     
2095     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2096
2097     NSEvent *oldCurrentEvent = _currentEvent;
2098     _currentEvent = KWQRetain(event);
2099     
2100     v->handleMouseMoveEvent(event);
2101     
2102     ASSERT(_currentEvent == event);
2103     KWQRelease(event);
2104     _currentEvent = oldCurrentEvent;
2105
2106     END_BLOCK_OBJC_EXCEPTIONS;
2107 }
2108
2109 // Called as we walk up the element chain for nodes with CSS property -khtml-user-drag == auto
2110 bool FrameMac::shouldDragAutoNode(Node* node, int x, int y) const
2111 {
2112     // We assume that WebKit only cares about dragging things that can be leaf nodes (text, images, urls).
2113     // This saves a bunch of expensive calls (creating WC and WK element dicts) as we walk farther up
2114     // the node hierarchy, and we also don't have to cook up a way to ask WK about non-leaf nodes
2115     // (since right now WK just hit-tests using a cached lastMouseDown).
2116     if (!node->hasChildNodes() && d->m_view) {
2117         int windowX, windowY;
2118         d->m_view->contentsToViewport(x, y, windowX, windowY);
2119         NSPoint eventLoc = {windowX, windowY};
2120         return [_bridge mayStartDragAtEventLocation:eventLoc];
2121     } else
2122         return NO;
2123 }
2124
2125 bool FrameMac::sendContextMenuEvent(NSEvent *event)
2126 {
2127     Document* doc = d->m_doc.get();
2128     FrameView* v = d->m_view.get();
2129     if (!doc || !v)
2130         return false;
2131
2132     bool swallowEvent;
2133     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2134
2135     NSEvent *oldCurrentEvent = _currentEvent;
2136     _currentEvent = KWQRetain(event);
2137     
2138     PlatformMouseEvent mouseEvent(event);
2139
2140     int xm, ym;
2141     v->viewportToContents(mouseEvent.x(), mouseEvent.y(), xm, ym);
2142
2143     MouseEventWithHitTestResults mev = doc->prepareMouseEvent(false, true, false, xm, ym, mouseEvent);
2144
2145     swallowEvent = v->dispatchMouseEvent(contextmenuEvent, mev.innerNode(), true, 0, mouseEvent, true);
2146     if (!swallowEvent && !isPointInsideSelection(xm, ym) &&
2147         ([_bridge selectWordBeforeMenuEvent] || [_bridge isEditable] || mev.innerNode()->isContentEditable())) {
2148         selectClosestWordFromMouseEvent(mouseEvent, mev.innerNode(), xm, ym);
2149     }
2150
2151     ASSERT(_currentEvent == event);
2152     KWQRelease(event);
2153     _currentEvent = oldCurrentEvent;
2154
2155     return swallowEvent;
2156
2157     END_BLOCK_OBJC_EXCEPTIONS;
2158
2159     return false;
2160 }
2161
2162 struct ListItemInfo {
2163     unsigned start;
2164     unsigned end;
2165 };
2166
2167 NSFileWrapper *FrameMac::fileWrapperForElement(Element *e)
2168 {
2169     NSFileWrapper *wrapper = nil;
2170     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2171     
2172     const AtomicString& attr = e->getAttribute(srcAttr);
2173     if (!attr.isEmpty()) {
2174         NSURL *URL = completeURL(attr.deprecatedString()).getNSURL();
2175         wrapper = [_bridge fileWrapperForURL:URL];
2176     }    
2177     if (!wrapper) {
2178         RenderImage *renderer = static_cast<RenderImage *>(e->renderer());
2179         if (renderer->cachedImage() && !renderer->cachedImage()->isErrorImage()) {
2180             wrapper = [[NSFileWrapper alloc] initRegularFileWithContents:(NSData*)(renderer->cachedImage()->image()->getTIFFRepresentation())];
2181             [wrapper setPreferredFilename:@"image.tiff"];
2182             [wrapper autorelease];
2183         }
2184     }
2185
2186     return wrapper;
2187
2188     END_BLOCK_OBJC_EXCEPTIONS;
2189
2190     return nil;
2191 }
2192
2193 static Element *listParent(Element *item)
2194 {
2195     while (!item->hasTagName(ulTag) && !item->hasTagName(olTag)) {
2196         item = static_cast<Element *>(item->parentNode());
2197         if (!item)
2198             break;
2199     }
2200     return item;
2201 }
2202
2203 static Node* isTextFirstInListItem(Node *e)
2204 {
2205     if (!e->isTextNode())
2206         return 0;
2207     Node* par = e->parentNode();
2208     while (par) {
2209         if (par->firstChild() != e)
2210             return 0;
2211         if (par->hasTagName(liTag))
2212             return par;
2213         e = par;
2214         par = par->parentNode();
2215     }
2216     return 0;
2217 }
2218
2219 // FIXME: This collosal function needs to be refactored into maintainable smaller bits.
2220
2221 #define BULLET_CHAR 0x2022
2222 #define SQUARE_CHAR 0x25AA
2223 #define CIRCLE_CHAR 0x25E6
2224
2225 NSAttributedString *FrameMac::attributedString(Node *_start, int startOffset, Node *endNode, int endOffset)
2226 {
2227     ListItemInfo info;
2228     NSMutableAttributedString *result;
2229     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2230
2231     Node * _startNode = _start;
2232
2233     if (_startNode == nil) {
2234         return nil;
2235     }
2236
2237     result = [[[NSMutableAttributedString alloc] init] autorelease];
2238
2239     bool hasNewLine = true;
2240     bool addedSpace = true;
2241     NSAttributedString *pendingStyledSpace = nil;
2242     bool hasParagraphBreak = true;
2243     const Element *linkStartNode = 0;
2244     unsigned linkStartLocation = 0;
2245     DeprecatedPtrList<Element> listItems;
2246     DeprecatedValueList<ListItemInfo> listItemLocations;
2247     float maxMarkerWidth = 0;
2248     
2249     Node *n = _startNode;
2250     
2251     // If the first item is the entire text of a list item, use the list item node as the start of the 
2252     // selection, not the text node.  The user's intent was probably to select the list.
2253     if (n->isTextNode() && startOffset == 0) {
2254         Node *startListNode = isTextFirstInListItem(_startNode);
2255         if (startListNode){
2256             _startNode = startListNode;
2257             n = _startNode;
2258         }
2259     }
2260     
2261     while (n) {
2262         RenderObject *renderer = n->renderer();
2263         if (renderer) {
2264             RenderStyle *style = renderer->style();
2265             NSFont *font = style->font().getNSFont();
2266             bool needSpace = pendingStyledSpace != nil;
2267             if (n->isTextNode()) {
2268                 if (hasNewLine) {
2269                     addedSpace = true;
2270                     needSpace = false;
2271                     [pendingStyledSpace release];
2272                     pendingStyledSpace = nil;
2273                     hasNewLine = false;
2274                 }
2275                 DeprecatedString text;
2276                 DeprecatedString str = n->nodeValue().deprecatedString();
2277                 int start = (n == _startNode) ? startOffset : -1;
2278                 int end = (n == endNode) ? endOffset : -1;
2279                 if (renderer->isText()) {
2280                     if (!style->collapseWhiteSpace()) {
2281                         if (needSpace && !addedSpace) {
2282                             if (text.isEmpty() && linkStartLocation == [result length]) {
2283                                 ++linkStartLocation;
2284                             }
2285                             [result appendAttributedString:pendingStyledSpace];
2286                         }
2287                         int runStart = (start == -1) ? 0 : start;
2288                         int runEnd = (end == -1) ? str.length() : end;
2289                         text += str.mid(runStart, runEnd-runStart);
2290                         [pendingStyledSpace release];
2291                         pendingStyledSpace = nil;
2292                         addedSpace = str[runEnd-1].direction() == QChar::DirWS;
2293                     }
2294                     else {
2295                         RenderText* textObj = static_cast<RenderText*>(renderer);
2296                         if (!textObj->firstTextBox() && str.length() > 0 && !addedSpace) {
2297                             // We have no runs, but we do have a length.  This means we must be
2298                             // whitespace that collapsed away at the end of a line.
2299                             text += ' ';
2300                             addedSpace = true;
2301                         }
2302                         else {
2303                             addedSpace = false;
2304                             for (InlineTextBox* box = textObj->firstTextBox(); box; box = box->nextTextBox()) {
2305                                 int runStart = (start == -1) ? box->m_start : start;
2306                                 int runEnd = (end == -1) ? box->m_start + box->m_len : end;
2307                                 runEnd = kMin(runEnd, box->m_start + box->m_len);
2308                                 if (runStart >= box->m_start &&
2309                                     runStart < box->m_start + box->m_len) {
2310                                     if (box == textObj->firstTextBox() && box->m_start == runStart && runStart > 0) {
2311                                         needSpace = true; // collapsed space at the start
2312                                     }
2313                                     if (needSpace && !addedSpace) {
2314                                         if (pendingStyledSpace != nil) {
2315                                             if (text.isEmpty() && linkStartLocation == [result length]) {
2316                                                 ++linkStartLocation;
2317                                             }
2318                                             [result appendAttributedString:pendingStyledSpace];
2319                                         } else {
2320                                             text += ' ';
2321                                         }
2322                                     }
2323                                     DeprecatedString runText = str.mid(runStart, runEnd - runStart);
2324                                     runText.replace('\n', ' ');
2325                                     text += runText;
2326                                     int nextRunStart = box->nextTextBox() ? box->nextTextBox()->m_start : str.length(); // collapsed space between runs or at the end
2327                                     needSpace = nextRunStart > runEnd;
2328                                     [pendingStyledSpace release];
2329                                     pendingStyledSpace = nil;
2330                                     addedSpace = str[runEnd-1].direction() == QChar::DirWS;
2331                                     start = -1;
2332                                 }
2333                                 if (end != -1 && runEnd >= end)
2334                                     break;
2335                             }
2336                         }
2337                     }
2338                 }
2339                 
2340                 text.replace(QChar('\\'), renderer->backslashAsCurrencySymbol());
2341     
2342                 if (text.length() > 0 || needSpace) {
2343                     NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init];
2344                     [attrs setObject:font forKey:NSFontAttributeName];
2345                     if (style && style->color().isValid() && style->color().alpha() != 0)
2346                         [attrs setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2347                     if (style && style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0)
2348                         [attrs setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2349
2350                     if (text.length() > 0) {
2351                         hasParagraphBreak = false;
2352                         NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString() attributes:attrs];
2353                         [result appendAttributedString: partialString];                
2354                         [partialString release];
2355                     }
2356
2357                     if (needSpace) {
2358                         [pendingStyledSpace release];
2359                         pendingStyledSpace = [[NSAttributedString alloc] initWithString:@" " attributes:attrs];
2360                     }
2361
2362                     [attrs release];
2363                 }
2364             } else {
2365                 // This is our simple HTML -> ASCII transformation:
2366                 DeprecatedString text;
2367                 if (n->hasTagName(aTag)) {
2368                     // Note the start of the <a> element.  We will add the NSLinkAttributeName
2369                     // attribute to the attributed string when navigating to the next sibling 
2370                     // of this node.
2371                     linkStartLocation = [result length];
2372                     linkStartNode = static_cast<Element*>(n);
2373                 } else if (n->hasTagName(brTag)) {
2374                     text += "\n";
2375                     hasNewLine = true;
2376                 } else if (n->hasTagName(liTag)) {
2377                     DeprecatedString listText;
2378                     Element *itemParent = listParent(static_cast<Element *>(n));
2379                     
2380                     if (!hasNewLine)
2381                         listText += '\n';
2382                     hasNewLine = true;
2383
2384                     listItems.append(static_cast<Element*>(n));
2385                     info.start = [result length];
2386                     info.end = 0;
2387                     listItemLocations.append (info);
2388                     
2389                     listText += '\t';
2390                     if (itemParent && renderer->isListItem()) {
2391                         RenderListItem *listRenderer = static_cast<RenderListItem*>(renderer);
2392
2393                         maxMarkerWidth = MAX([font pointSize], maxMarkerWidth);
2394                         switch(style->listStyleType()) {
2395                             case WebCore::DISC:
2396                                 listText += ((QChar)BULLET_CHAR);
2397                                 break;
2398                             case WebCore::CIRCLE:
2399                                 listText += ((QChar)CIRCLE_CHAR);
2400                                 break;
2401                             case WebCore::SQUARE:
2402                                 listText += ((QChar)SQUARE_CHAR);
2403                                 break;
2404                             case WebCore::LNONE:
2405                                 break;
2406                             default:
2407                                 DeprecatedString marker = listRenderer->markerStringValue();
2408                                 listText += marker;
2409                                 // Use AppKit metrics.  Will be rendered by AppKit.
2410                                 float markerWidth = [marker.getNSString() sizeWithAttributes:[NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]].width;
2411                                 maxMarkerWidth = MAX(markerWidth, maxMarkerWidth);
2412                         }
2413
2414                         listText += ' ';
2415                         listText += '\t';
2416
2417                         NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init];
2418                         [attrs setObject:font forKey:NSFontAttributeName];
2419                         if (style && style->color().isValid())
2420                             [attrs setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2421                         if (style && style->backgroundColor().isValid())
2422                             [attrs setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2423
2424                         NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:listText.getNSString() attributes:attrs];
2425                         [attrs release];
2426                         [result appendAttributedString: partialString];                
2427                         [partialString release];
2428                     }
2429                 } else if (n->hasTagName(olTag) || n->hasTagName(ulTag)) {
2430                     if (!hasNewLine)
2431                         text += "\n";
2432                     hasNewLine = true;
2433                 } else if (n->hasTagName(tdTag) ||
2434                            n->hasTagName(thTag) ||
2435                            n->hasTagName(hrTag) ||
2436                            n->hasTagName(ddTag) ||
2437                            n->hasTagName(dlTag) ||
2438                            n->hasTagName(dtTag) ||
2439                            n->hasTagName(preTag) ||
2440                            n->hasTagName(blockquoteTag) ||
2441                            n->hasTagName(divTag)) {
2442                     if (!hasNewLine)
2443                         text += '\n';
2444                     hasNewLine = true;
2445                 } else if (n->hasTagName(pTag) ||
2446                            n->hasTagName(trTag) ||
2447                            n->hasTagName(h1Tag) ||
2448                            n->hasTagName(h2Tag) ||
2449                            n->hasTagName(h3Tag) ||
2450                            n->hasTagName(h4Tag) ||
2451                            n->hasTagName(h5Tag) ||
2452                            n->hasTagName(h6Tag)) {
2453                     if (!hasNewLine)
2454                         text += '\n';
2455                     
2456                     // In certain cases, emit a paragraph break.
2457                     int bottomMargin = renderer->collapsedMarginBottom();
2458                     int fontSize = style->fontDescription().computedPixelSize();
2459                     if (bottomMargin * 2 >= fontSize) {
2460                         if (!hasParagraphBreak) {
2461                             text += '\n';
2462                             hasParagraphBreak = true;
2463                         }
2464                     }
2465                     
2466                     hasNewLine = true;
2467                 }
2468                 else if (n->hasTagName(imgTag)) {
2469                     if (pendingStyledSpace != nil) {
2470                         if (linkStartLocation == [result length]) {
2471                             ++linkStartLocation;
2472                         }
2473                         [result appendAttributedString:pendingStyledSpace];
2474                         [pendingStyledSpace release];
2475                         pendingStyledSpace = nil;
2476                     }
2477                     NSFileWrapper *fileWrapper = fileWrapperForElement(static_cast<Element *>(n));
2478                     NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWrapper:fileWrapper];
2479                     NSAttributedString *iString = [NSAttributedString attributedStringWithAttachment:attachment];
2480                     [result appendAttributedString: iString];
2481                     [attachment release];
2482                 }
2483
2484                 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()];
2485                 [result appendAttributedString: partialString];
2486                 [partialString release];
2487             }
2488         }
2489
2490         if (n == endNode)
2491             break;
2492
2493         Node *next = n->firstChild();
2494         if (!next) {
2495             next = n->nextSibling();
2496         }
2497
2498         while (!next && n->parentNode()) {
2499             DeprecatedString text;
2500             n = n->parentNode();
2501             if (n == endNode)
2502                 break;
2503             next = n->nextSibling();
2504
2505             if (n->hasTagName(aTag)) {
2506                 // End of a <a> element.  Create an attributed string NSLinkAttributeName attribute
2507                 // for the range of the link.  Note that we create the attributed string from the DOM, which
2508                 // will have corrected any illegally nested <a> elements.
2509                 if (linkStartNode && n == linkStartNode) {
2510                     String href = parseURL(linkStartNode->getAttribute(hrefAttr));
2511                     KURL kURL = Mac(linkStartNode->getDocument()->frame())->completeURL(href.deprecatedString());
2512                     
2513                     NSURL *URL = kURL.getNSURL();
2514                     NSRange tempRange = { linkStartLocation, [result length]-linkStartLocation }; // workaround for 4213314
2515                     [result addAttribute:NSLinkAttributeName value:URL range:tempRange];
2516                     linkStartNode = 0;
2517                 }
2518             }
2519             else if (n->hasTagName(olTag) || n->hasTagName(ulTag)) {
2520                 if (!hasNewLine)
2521                     text += '\n';
2522                 hasNewLine = true;
2523             } else if (n->hasTagName(liTag)) {
2524                 
2525                 int i, count = listItems.count();
2526                 for (i = 0; i < count; i++){
2527                     if (listItems.at(i) == n){
2528                         listItemLocations[i].end = [result length];
2529                         break;
2530                     }
2531                 }
2532                 if (!hasNewLine)
2533                     text += '\n';
2534                 hasNewLine = true;
2535             } else if (n->hasTagName(tdTag) ||
2536                        n->hasTagName(thTag) ||
2537                        n->hasTagName(hrTag) ||
2538                        n->hasTagName(ddTag) ||
2539                        n->hasTagName(dlTag) ||
2540                        n->hasTagName(dtTag) ||
2541                        n->hasTagName(preTag) ||
2542                        n->hasTagName(blockquoteTag) ||
2543                        n->hasTagName(divTag)) {
2544                 if (!hasNewLine)
2545                     text += '\n';
2546                 hasNewLine = true;
2547             } else if (n->hasTagName(pTag) ||
2548                        n->hasTagName(trTag) ||
2549                        n->hasTagName(h1Tag) ||
2550                        n->hasTagName(h2Tag) ||
2551                        n->hasTagName(h3Tag) ||
2552                        n->hasTagName(h4Tag) ||
2553                        n->hasTagName(h5Tag) ||
2554                        n->hasTagName(h6Tag)) {
2555                 if (!hasNewLine)
2556                     text += '\n';
2557                 // An extra newline is needed at the start, not the end, of these types of tags,
2558                 // so don't add another here.
2559                 hasNewLine = true;
2560             }
2561             
2562             NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()];
2563             [result appendAttributedString:partialString];
2564             [partialString release];
2565         }
2566
2567         n = next;
2568     }
2569     
2570     [pendingStyledSpace release];
2571     
2572     // Apply paragraph styles from outside in.  This ensures that nested lists correctly
2573     // override their parent's paragraph style.
2574     {
2575         unsigned i, count = listItems.count();
2576         Element *e;
2577
2578 #ifdef POSITION_LIST
2579         Node *containingBlock;
2580         int containingBlockX, containingBlockY;
2581         
2582         // Determine the position of the outermost containing block.  All paragraph
2583         // styles and tabs should be relative to this position.  So, the horizontal position of 
2584         // each item in the list (in the resulting attributed string) will be relative to position 
2585         // of the outermost containing block.
2586         if (count > 0){
2587             containingBlock = _startNode;
2588             while (containingBlock->renderer()->isInline()){
2589                 containingBlock = containingBlock->parentNode();
2590             }
2591             containingBlock->renderer()->absolutePosition(containingBlockX, containingBlockY);
2592         }
2593 #endif
2594         
2595         for (i = 0; i < count; i++){
2596             e = listItems.at(i);
2597             info = listItemLocations[i];
2598             
2599             if (info.end < info.start)
2600                 info.end = [result length];
2601                 
2602             RenderObject *r = e->renderer();
2603             RenderStyle *style = r->style();
2604
2605             int rx;
2606             NSFont *font = style->font().getNSFont();
2607             float pointSize = [font pointSize];
2608
2609 #ifdef POSITION_LIST
2610             int ry;
2611             r->absolutePosition(rx, ry);
2612             rx -= containingBlockX;
2613             
2614             // Ensure that the text is indented at least enough to allow for the markers.
2615             rx = MAX(rx, (int)maxMarkerWidth);
2616 #else
2617             rx = (int)MAX(maxMarkerWidth, pointSize);
2618 #endif
2619
2620             // The bullet text will be right aligned at the first tab marker, followed
2621             // by a space, followed by the list item text.  The space is arbitrarily
2622             // picked as pointSize*2/3.  The space on the first line of the text item
2623             // is established by a left aligned tab, on subsequent lines it's established
2624             // by the head indent.
2625             NSMutableParagraphStyle *mps = [[NSMutableParagraphStyle alloc] init];
2626             [mps setFirstLineHeadIndent: 0];
2627             [mps setHeadIndent: rx];
2628             [mps setTabStops:[NSArray arrayWithObjects:
2629                         [[[NSTextTab alloc] initWithType:NSRightTabStopType location:rx-(pointSize*2/3)] autorelease],
2630                         [[[NSTextTab alloc] initWithType:NSLeftTabStopType location:rx] autorelease],
2631                         nil]];
2632             NSRange tempRange = { info.start, info.end-info.start }; // workaround for 4213314
2633             [result addAttribute:NSParagraphStyleAttributeName value:mps range:tempRange];
2634             [mps release];
2635         }
2636     }
2637
2638     return result;
2639
2640     END_BLOCK_OBJC_EXCEPTIONS;
2641
2642     return nil;
2643 }
2644
2645 NSImage *FrameMac::imageFromRect(NSRect rect) const
2646 {
2647     NSView *view = d->m_view->getDocumentView();
2648     if (!view)
2649         return nil;
2650     
2651     NSImage *resultImage;
2652     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2653     
2654     NSRect bounds = [view bounds];
2655     resultImage = [[[NSImage alloc] initWithSize:rect.size] autorelease];
2656
2657     if (rect.size.width != 0 && rect.size.height != 0) {
2658         [resultImage setFlipped:YES];
2659         [resultImage lockFocus];
2660
2661         CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
2662
2663         CGContextSaveGState(context);
2664         CGContextTranslateCTM(context, bounds.origin.x - rect.origin.x, bounds.origin.y - rect.origin.y);
2665         [view drawRect:rect];
2666         CGContextRestoreGState(context);
2667
2668         [resultImage unlockFocus];
2669         [resultImage setFlipped:NO];
2670     }
2671
2672     return resultImage;
2673
2674     END_BLOCK_OBJC_EXCEPTIONS;
2675     
2676     return nil;
2677 }
2678
2679 NSImage *FrameMac::selectionImage() const
2680 {
2681     d->m_drawSelectionOnly = true;  // invoke special drawing mode
2682     NSImage *result = imageFromRect(visibleSelectionRect());
2683     d->m_drawSelectionOnly = false;
2684     return result;
2685 }
2686
2687 NSImage *FrameMac::snapshotDragImage(Node *node, NSRect *imageRect, NSRect *elementRect) const
2688 {
2689     RenderObject *renderer = node->renderer();
2690     if (!renderer)
2691         return nil;
2692     
2693     renderer->updateDragState(true);    // mark dragged nodes (so they pick up the right CSS)
2694     d->m_doc->updateLayout();        // forces style recalc - needed since changing the drag state might
2695                                         // imply new styles, plus JS could have changed other things
2696     IntRect topLevelRect;
2697     NSRect paintingRect = renderer->paintingRootRect(topLevelRect);
2698
2699     d->m_elementToDraw = node;              // invoke special sub-tree drawing mode
2700     NSImage *result = imageFromRect(paintingRect);
2701     renderer->updateDragState(false);
2702     d->m_doc->updateLayout();
2703     d->m_elementToDraw = 0;
2704
2705     if (elementRect)
2706         *elementRect = topLevelRect;
2707     if (imageRect)
2708         *imageRect = paintingRect;
2709     return result;
2710 }
2711
2712 NSFont *FrameMac::fontForSelection(bool *hasMultipleFonts) const
2713 {
2714     if (hasMultipleFonts)
2715         *hasMultipleFonts = false;
2716
2717     if (!d->m_selection.isRange()) {
2718         Node *nodeToRemove;
2719         RenderStyle *style = styleForSelectionStart(nodeToRemove); // sets nodeToRemove
2720
2721         NSFont *result = 0;
2722         if (style)
2723             result = style->font().getNSFont();
2724         
2725         if (nodeToRemove) {
2726             ExceptionCode ec;
2727             nodeToRemove->remove(ec);
2728             ASSERT(ec == 0);
2729         }
2730
2731         return result;
2732     }
2733
2734     NSFont *font = nil;
2735
2736     RefPtr<Range> range = d->m_selection.toRange();
2737     Node *startNode = range->editingStartPosition().node();
2738     if (startNode != nil) {
2739         Node *pastEnd = range->pastEndNode();
2740         // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one
2741         // unreproducible case where this didn't happen, so check for nil also.
2742         for (Node *n = startNode; n && n != pastEnd; n = n->traverseNextNode()) {
2743             RenderObject *renderer = n->renderer();
2744             if (!renderer)
2745                 continue;
2746             // FIXME: Are there any node types that have renderers, but that we should be skipping?
2747             NSFont *f = renderer->style()->font().getNSFont();
2748             if (font == nil) {
2749                 font = f;
2750                 if (!hasMultipleFonts)
2751                     break;
2752             } else if (font != f) {
2753                 *hasMultipleFonts = true;
2754                 break;
2755             }
2756         }
2757     }
2758
2759     return font;
2760 }
2761
2762 NSDictionary *FrameMac::fontAttributesForSelectionStart() const
2763 {
2764     Node *nodeToRemove;
2765     RenderStyle *style = styleForSelectionStart(nodeToRemove);
2766     if (!style)
2767         return nil;
2768
2769     NSMutableDictionary *result = [NSMutableDictionary dictionary];
2770
2771     if (style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0)
2772         [result setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2773
2774     if (style->font().getNSFont())
2775         [result setObject:style->font().getNSFont() forKey:NSFontAttributeName];
2776
2777     if (style->color().isValid() && style->color() != Color::black)
2778         [result setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2779
2780     ShadowData *shadow = style->textShadow();
2781     if (shadow) {
2782         NSShadow *s = [[NSShadow alloc] init];
2783         [s setShadowOffset:NSMakeSize(shadow->x, shadow->y)];
2784         [s setShadowBlurRadius:shadow->blur];
2785         [s setShadowColor:nsColor(shadow->color)];
2786         [result setObject:s forKey:NSShadowAttributeName];
2787     }
2788
2789     int decoration = style->textDecorationsInEffect();
2790     if (decoration & WebCore::LINE_THROUGH)
2791         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSStrikethroughStyleAttributeName];
2792
2793     int superscriptInt = 0;
2794     switch (style->verticalAlign()) {
2795         case WebCore::BASELINE:
2796         case WebCore::BOTTOM:
2797         case WebCore::BASELINE_MIDDLE:
2798         case WebCore::LENGTH:
2799         case WebCore::MIDDLE:
2800         case WebCore::TEXT_BOTTOM:
2801         case WebCore::TEXT_TOP:
2802         case WebCore::TOP:
2803             break;
2804         case WebCore::SUB:
2805             superscriptInt = -1;
2806             break;
2807         case WebCore::SUPER:
2808             superscriptInt = 1;
2809             break;
2810     }
2811     if (superscriptInt)
2812         [result setObject:[NSNumber numberWithInt:superscriptInt] forKey:NSSuperscriptAttributeName];
2813
2814     if (decoration & WebCore::UNDERLINE)
2815         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
2816
2817     if (nodeToRemove) {
2818         ExceptionCode ec = 0;
2819         nodeToRemove->remove(ec);
2820         ASSERT(ec == 0);
2821     }
2822
2823     return result;
2824 }
2825
2826 NSWritingDirection FrameMac::baseWritingDirectionForSelectionStart() const
2827 {
2828     NSWritingDirection result = NSWritingDirectionLeftToRight;
2829
2830     Position pos = VisiblePosition(d->m_selection.start(), d->m_selection.affinity()).deepEquivalent();
2831     Node *node = pos.node();
2832     if (!node || !node->renderer() || !node->renderer()->containingBlock())
2833         return result;
2834     RenderStyle *style = node->renderer()->containingBlock()->style();
2835     if (!style)
2836         return result;
2837         
2838     switch (style->direction()) {
2839         case WebCore::LTR:
2840             result = NSWritingDirectionLeftToRight;
2841             break;
2842         case WebCore::RTL:
2843             result = NSWritingDirectionRightToLeft;
2844             break;
2845     }
2846
2847     return result;
2848 }
2849
2850 void FrameMac::tokenizerProcessedData()
2851 {
2852     if (d->m_doc) {
2853         checkCompleted();
2854     }
2855     [_bridge tokenizerProcessedData];
2856 }
2857
2858 void FrameMac::setBridge(WebCoreFrameBridge *bridge)
2859
2860     if (_bridge == bridge)
2861         return;
2862
2863     KWQRetain(bridge);
2864     KWQRelease(_bridge);
2865     _bridge = bridge;
2866 }
2867
2868 DeprecatedString FrameMac::overrideMediaType() const
2869 {
2870     NSString *overrideType = [_bridge overrideMediaType];
2871     if (overrideType)
2872         return DeprecatedString::fromNSString(overrideType);
2873     return DeprecatedString();
2874 }
2875
2876 void FrameMac::setDisplaysWithFocusAttributes(bool flag)
2877 {
2878     if (d->m_isFocused == flag)
2879         return;
2880     
2881     Frame::setDisplaysWithFocusAttributes(flag);
2882     Document *doc = document();
2883     // Mac Specific: Changing the tint of controls from clear to aqua/graphite and vice versa.  We
2884     // do a "fake" paint.  When the theme gets a paint call, it can then do an invalidate.
2885     if (doc && d->m_view && d->m_view->getDocumentView() && theme()->supportsControlTints() && renderer()) {
2886         doc->updateLayout(); // Ensure layout is up to date.
2887         IntRect visibleRect(enclosingIntRect(d->m_view->visibleContentRect()));
2888         GraphicsContext p;
2889         p.setUpdatingControlTints(true);
2890         paint(&p, visibleRect);
2891     }
2892 }
2893
2894 NSColor *FrameMac::bodyBackgroundColor() const
2895 {
2896     if (document() && document()->body() && document()->body()->renderer()) {
2897         Color bgColor = document()->body()->renderer()->style()->backgroundColor();
2898         if (bgColor.isValid())
2899             return nsColor(bgColor);
2900     }
2901     return nil;
2902 }
2903
2904 WebCoreKeyboardUIMode FrameMac::keyboardUIMode() const
2905 {
2906     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2907     return [_bridge keyboardUIMode];
2908     END_BLOCK_OBJC_EXCEPTIONS;
2909
2910     return WebCoreKeyboardAccessDefault;
2911 }
2912
2913 void FrameMac::didTellBridgeAboutLoad(const WebCore::String& URL)
2914 {
2915     urlsBridgeKnowsAbout.add(URL.impl());
2916 }
2917
2918 bool FrameMac::haveToldBridgeAboutLoad(const WebCore::String& URL)
2919 {
2920     return urlsBridgeKnowsAbout.contains(URL.impl());
2921 }
2922
2923 void FrameMac::clear()
2924 {
2925     urlsBridgeKnowsAbout.clear();
2926     setMarkedTextRange(0, nil, nil);
2927     Frame::clear();
2928 }
2929
2930 void FrameMac::print()
2931 {
2932     [Mac(this)->_bridge print];
2933 }
2934
2935 KJS::Bindings::Instance *FrameMac::getAppletInstanceForWidget(Widget *widget)
2936 {
2937     NSView *aView = widget->getView();
2938     if (!aView)
2939         return 0;
2940     jobject applet;
2941     
2942     // Get a pointer to the actual Java applet instance.
2943     if ([_bridge respondsToSelector:@selector(getAppletInView:)])
2944         applet = [_bridge getAppletInView:aView];
2945     else
2946         applet = [_bridge pollForAppletInView:aView];
2947     
2948     if (applet) {
2949         // Wrap the Java instance in a language neutral binding and hand
2950         // off ownership to the APPLET element.
2951         KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2952         KJS::Bindings::Instance *instance = KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::JavaLanguage, applet, executionContext);        
2953         return instance;
2954     }
2955     
2956     return 0;
2957 }
2958
2959 static KJS::Bindings::Instance *getInstanceForView(NSView *aView)
2960 {
2961     if ([aView respondsToSelector:@selector(objectForWebScript)]){
2962         id object = [aView objectForWebScript];
2963         if (object) {
2964             KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2965             return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::ObjectiveCLanguage, object, executionContext);
2966         }
2967     }
2968     else if ([aView respondsToSelector:@selector(pluginScriptableObject)]){
2969         void *object = [aView pluginScriptableObject];
2970         if (object) {
2971             KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2972             return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::CLanguage, object, executionContext);
2973         }
2974     }
2975     return 0;
2976 }
2977
2978 KJS::Bindings::Instance *FrameMac::getEmbedInstanceForWidget(Widget *widget)
2979 {
2980     return getInstanceForView(widget->getView());
2981 }
2982
2983 KJS::Bindings::Instance *FrameMac::getObjectInstanceForWidget(Widget *widget)
2984 {
2985     return getInstanceForView(widget->getView());
2986 }
2987
2988 void FrameMac::addPluginRootObject(const KJS::Bindings::RootObject *root)
2989 {
2990     rootObjects.append (root);
2991 }
2992
2993 void FrameMac::cleanupPluginRootObjects()
2994 {
2995     JSLock lock;
2996
2997     KJS::Bindings::RootObject *root;
2998     while ((root = rootObjects.getLast())) {
2999         root->removeAllNativeReferences();
3000         rootObjects.removeLast();
3001     }
3002 }
3003
3004 void FrameMac::registerCommandForUndoOrRedo(const EditCommandPtr &cmd, bool isRedo)
3005 {
3006     ASSERT(cmd.get());
3007     KWQEditCommand *kwq = [KWQEditCommand commandWithEditCommand:cmd.get()];
3008     NSUndoManager *undoManager = [_bridge undoManager];
3009     [undoManager registerUndoWithTarget:_bridge selector:(isRedo ? @selector(redoEditing:) : @selector(undoEditing:)) object:kwq];
3010     NSString *actionName = [_bridge nameForUndoAction:static_cast<WebUndoAction>(cmd.editingAction())];
3011     if (actionName != nil) {
3012         [undoManager setActionName:actionName];
3013     }
3014     _haveUndoRedoOperations = YES;
3015 }
3016
3017 void FrameMac::registerCommandForUndo(const EditCommandPtr &cmd)
3018 {
3019     registerCommandForUndoOrRedo(cmd, NO);
3020 }
3021
3022 void FrameMac::registerCommandForRedo(const EditCommandPtr &cmd)
3023 {
3024     registerCommandForUndoOrRedo(cmd, YES);
3025 }
3026
3027 void FrameMac::clearUndoRedoOperations()
3028 {
3029     if (_haveUndoRedoOperations) {
3030         [[_bridge undoManager] removeAllActionsWithTarget:_bridge];
3031         _haveUndoRedoOperations = NO;
3032     }
3033 }
3034
3035 void FrameMac::issueUndoCommand()
3036 {
3037     if (canUndo())
3038         [[_bridge undoManager] undo];
3039 }
3040
3041 void FrameMac::issueRedoCommand()
3042 {
3043     if (canRedo())
3044         [[_bridge undoManager] redo];
3045 }
3046
3047 void FrameMac::issueCutCommand()
3048 {
3049     [_bridge issueCutCommand];
3050 }
3051
3052 void FrameMac::issueCopyCommand()
3053 {
3054     [_bridge issueCopyCommand];
3055 }
3056
3057 void FrameMac::issuePasteCommand()
3058 {
3059     [_bridge issuePasteCommand];
3060 }
3061
3062 void FrameMac::issuePasteAndMatchStyleCommand()
3063 {
3064     [_bridge issuePasteAndMatchStyleCommand];
3065 }
3066
3067 void FrameMac::issueTransposeCommand()
3068 {
3069     [_bridge issueTransposeCommand];
3070 }
3071
3072 bool FrameMac::canUndo() const
3073 {
3074     return [[Mac(this)->_bridge undoManager] canUndo];
3075 }
3076
3077 bool FrameMac::canRedo() const
3078 {
3079     return [[Mac(this)->_bridge undoManager] canRedo];
3080 }
3081
3082 bool FrameMac::canPaste() const
3083 {
3084     return [Mac(this)->_bridge canPaste];
3085 }
3086
3087 void FrameMac::markMisspellingsInAdjacentWords(const VisiblePosition &p)
3088 {
3089     if (![_bridge isContinuousSpellCheckingEnabled])
3090         return;
3091     markMisspellings(SelectionController(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)));
3092 }
3093
3094 void FrameMac::markMisspellings(const SelectionController &selection)
3095 {
3096     // This function is called with a selection already expanded to word boundaries.
3097     // Might be nice to assert that here.
3098
3099     if (![_bridge isContinuousSpellCheckingEnabled])
3100         return;
3101
3102     RefPtr<Range> searchRange(selection.toRange());
3103     if (!searchRange || searchRange->isDetached())
3104         return;
3105     
3106     // If we're not in an editable node, bail.
3107     int exception = 0;
3108     Node *editableNode = searchRange->startContainer(exception);
3109     if (!editableNode->isContentEditable())
3110         return;
3111     
3112     // Get the spell checker if it is available
3113     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
3114     if (checker == nil)
3115         return;
3116     
3117     WordAwareIterator it(searchRange.get());
3118     
3119     while (!it.atEnd()) {      // we may be starting at the end of the doc, and already by atEnd
3120         const QChar *chars = it.characters();
3121         int len = it.length();
3122         if (len > 1 || !chars[0].isSpace()) {
3123             NSString *chunk = [[NSString alloc] initWithCharactersNoCopy:(unichar *)chars length:len freeWhenDone:NO];
3124             int startIndex = 0;
3125             // Loop over the chunk to find each misspelling in it.
3126             while (startIndex < len) {
3127                 NSRange misspelling = [checker checkSpellingOfString:chunk startingAt:startIndex language:nil wrap:NO inSpellDocumentWithTag:[_bridge spellCheckerDocumentTag] wordCount:NULL];
3128                 if (misspelling.length == 0) {
3129                     break;
3130                 }
3131                 else {
3132                     // Build up result range and string.  Note the misspelling may span many text nodes,
3133                     // but the CharIterator insulates us from this complexity
3134                     RefPtr<Range> misspellingRange(rangeOfContents(document()));
3135                     CharacterIterator chars(it.range().get());
3136                     chars.advance(misspelling.location);
3137                     misspellingRange->setStart(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
3138                     chars.advance(misspelling.length);
3139                     misspellingRange->setEnd(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
3140                     // Mark misspelling in document.
3141                     document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
3142                     startIndex = misspelling.location + misspelling.length;
3143                 }
3144             }
3145             [chunk release];
3146         }
3147     
3148         it.advance();
3149     }
3150 }
3151
3152 void FrameMac::respondToChangedSelection(const SelectionController &oldSelection, bool closeTyping)
3153 {
3154     if (document()) {
3155         if ([_bridge isContinuousSpellCheckingEnabled]) {
3156             SelectionController oldAdjacentWords = SelectionController();
3157             
3158             // If this is a change in selection resulting from a delete operation, oldSelection may no longer
3159             // be in the document.
3160             if (oldSelection.start().node() && oldSelection.start().node()->inDocument()) {
3161                 VisiblePosition oldStart(oldSelection.start(), oldSelection.affinity());
3162                 oldAdjacentWords = SelectionController(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));   
3163             }
3164
3165             VisiblePosition newStart(selection().start(), selection().affinity());
3166             SelectionController newAdjacentWords(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
3167
3168             if (oldAdjacentWords != newAdjacentWords) {
3169                 // Mark misspellings in the portion that was previously unmarked because of
3170                 // the proximity of the start of the selection. We only spell check words in
3171                 // the vicinity of the start of the old selection because the spelling checker
3172                 // is not fast enough to do a lot of spelling checking implicitly. This matches
3173                 // AppKit. This function is really the only code that knows that rule. The
3174                 // markMisspellings function is prepared to handler larger ranges.
3175
3176                 // When typing we check spelling elsewhere, so don't redo it here.
3177                 if (closeTyping) {
3178                     markMisspellings(oldAdjacentWords);
3179                 }
3180
3181                 // This only erases a marker in the first word of the selection.
3182                 // Perhaps peculiar, but it matches AppKit.
3183                 document()->removeMarkers(newAdjacentWords.toRange().get(), DocumentMarker::Spelling);
3184             }
3185         } else {
3186             // When continuous spell checking is off, no markers appear after the selection changes.
3187             document()->removeMarkers(DocumentMarker::Spelling);
3188         }
3189     }
3190
3191     [_bridge respondToChangedSelection];
3192 }
3193
3194 bool FrameMac::shouldChangeSelection(const SelectionController &oldSelection, const SelectionController &newSelection, WebCore::EAffinity affinity, bool stillSelecting) const
3195 {
3196     return [_bridge shouldChangeSelectedDOMRange:[DOMRange _rangeWith:oldSelection.toRange().get()]
3197                                       toDOMRange:[DOMRange _rangeWith:newSelection.toRange().get()]
3198                                         affinity:static_cast<NSSelectionAffinity>(affinity)
3199                                   stillSelecting:stillSelecting];
3200 }
3201
3202 void FrameMac::respondToChangedContents()
3203 {
3204     if (AccessibilityObjectCache::accessibilityEnabled())
3205         renderer()->document()->getAccObjectCache()->postNotificationToTopWebArea(renderer(), "AXValueChanged");
3206     [_bridge respondToChangedContents];
3207 }
3208
3209 bool FrameMac::isContentEditable() const
3210 {
3211     return Frame::isContentEditable() || [_bridge isEditable];
3212 }
3213
3214 bool FrameMac::shouldBeginEditing(const Range *range) const
3215 {
3216     ASSERT(range);
3217     return [_bridge shouldBeginEditing:[DOMRange _rangeWith:const_cast<Range *>(range)]];
3218 }
3219
3220 bool FrameMac::shouldEndEditing(const Range *range) const
3221 {
3222     ASSERT(range);
3223     return [_bridge shouldEndEditing:[DOMRange _rangeWith:const_cast<Range *>(range)]];
3224 }
3225
3226 void FrameMac::didBeginEditing() const
3227 {
3228     [_bridge didBeginEditing];
3229 }
3230
3231 void FrameMac::didEndEditing() const
3232 {
3233     [_bridge didEndEditing];
3234 }
3235
3236 static DeprecatedValueList<MarkedTextUnderline> convertAttributesToUnderlines(const Range *markedTextRange, NSArray *attributes, NSArray *ranges)
3237 {
3238     DeprecatedValueList<MarkedTextUnderline> result;
3239
3240     int exception = 0;
3241     int baseOffset = markedTextRange->startOffset(exception);
3242
3243     unsigned length = [attributes count];
3244     ASSERT([ranges count] == length);
3245
3246     for (unsigned i = 0; i < length; i++) {
3247         NSNumber *style = [[attributes objectAtIndex:i] objectForKey:NSUnderlineStyleAttributeName];
3248         if (!style)
3249             continue;
3250         NSRange range = [[ranges objectAtIndex:i] rangeValue];
3251         NSColor *color = [[attributes objectAtIndex:i] objectForKey:NSUnderlineColorAttributeName];
3252         Color qColor = Color::black;
3253         if (color) {
3254             NSColor* deviceColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
3255             qColor = Color(makeRGBA((int)(255 * [deviceColor redComponent]),
3256                                     (int)(255 * [deviceColor blueComponent]),
3257                                     (int)(255 * [deviceColor greenComponent]),
3258                                     (int)(255 * [deviceColor alphaComponent])));
3259         }
3260
3261         result.append(MarkedTextUnderline(range.location + baseOffset, 
3262                                           range.location + baseOffset + range.length, 
3263                                           qColor,
3264                                           [style intValue] > 1));
3265     }
3266
3267     return result;
3268 }
3269
3270 void FrameMac::setMarkedTextRange(const Range *range, NSArray *attributes, NSArray *ranges)
3271 {
3272     int exception = 0;
3273
3274     ASSERT(!range || range->startContainer(exception) == range->endContainer(exception));
3275     ASSERT(!range || range->collapsed(exception) || range->startContainer(exception)->isTextNode());
3276
3277     if (attributes == nil) {
3278         d->m_markedTextUsesUnderlines = false;
3279         d->m_markedTextUnderlines.clear();
3280     } else {
3281         d->m_markedTextUsesUnderlines = true;
3282         d->m_markedTextUnderlines = convertAttributesToUnderlines(range, attributes, ranges);
3283     }
3284
3285     if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
3286         m_markedTextRange->startContainer(exception)->renderer()->repaint();
3287
3288     if (range && range->collapsed(exception))
3289         m_markedTextRange = 0;
3290     else
3291         m_markedTextRange = const_cast<Range *>(range);
3292
3293     if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
3294         m_markedTextRange->startContainer(exception)->renderer()->repaint();
3295 }
3296
3297 bool FrameMac::canGoBackOrForward(int distance) const
3298 {
3299     return [_bridge canGoBackOrForward:distance];
3300 }
3301
3302 void FrameMac::didFirstLayout()
3303 {
3304     [_bridge didFirstLayout];
3305 }
3306
3307 NSMutableDictionary *FrameMac::dashboardRegionsDictionary()
3308 {
3309     Document *doc = document();
3310     if (!doc)
3311         return nil;
3312
3313     const DeprecatedValueList<DashboardRegionValue> regions = doc->dashboardRegions();
3314     unsigned i, count = regions.count();
3315
3316     // Convert the DeprecatedValueList<DashboardRegionValue> into a NSDictionary of WebDashboardRegions
3317     NSMutableDictionary *webRegions = [[[NSMutableDictionary alloc] initWithCapacity:count] autorelease];
3318     for (i = 0; i < count; i++) {
3319         DashboardRegionValue region = regions[i];
3320
3321         if (region.type == StyleDashboardRegion::None)
3322             continue;
3323             
3324         NSRect clip;
3325         clip.origin.x = region.clip.x();
3326         clip.origin.y = region.clip.y();
3327         clip.size.width = region.clip.width();
3328         clip.size.height = region.clip.height();
3329         NSRect rect;
3330         rect.origin.x = region.bounds.x();
3331         rect.origin.y = region.bounds.y();
3332         rect.size.width = region.bounds.width();
3333         rect.size.height = region.bounds.height();
3334         NSString *label = region.label.getNSString();
3335         WebDashboardRegionType type = WebDashboardRegionTypeNone;
3336         if (region.type == StyleDashboardRegion::Circle)
3337             type = WebDashboardRegionTypeCircle;
3338         else if (region.type == StyleDashboardRegion::Rectangle)
3339             type = WebDashboardRegionTypeRectangle;
3340         NSMutableArray *regionValues = [webRegions objectForKey:label];
3341         if (!regionValues) {
3342             regionValues = [NSMutableArray array];
3343             [webRegions setObject:regionValues forKey:label];
3344         }
3345         
3346         WebDashboardRegion *webRegion = [[[WebDashboardRegion alloc] initWithRect:rect clip:clip type:type] autorelease];
3347         [regionValues addObject:webRegion];
3348     }
3349     
3350     return webRegions;
3351 }
3352
3353 void FrameMac::dashboardRegionsChanged()
3354 {
3355     NSMutableDictionary *webRegions = dashboardRegionsDictionary();
3356     [_bridge dashboardRegionsChanged:webRegions];
3357 }
3358
3359 bool FrameMac::isCharacterSmartReplaceExempt(const QChar &c, bool isPreviousChar)
3360 {
3361     return [_bridge isCharacterSmartReplaceExempt:c.unicode() isPreviousCharacter:isPreviousChar];
3362 }
3363
3364 void FrameMac::handledOnloadEvents()
3365 {
3366     [_bridge handledOnloadEvents];
3367 }
3368
3369 bool FrameMac::shouldClose()
3370 {
3371     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3372
3373     if (![_bridge canRunBeforeUnloadConfirmPanel])
3374         return true;
3375
3376     RefPtr<Document> doc = document();
3377     if (!doc)
3378         return true;
3379     HTMLElement* body = doc->body();
3380     if (!body)
3381         return true;
3382
3383     RefPtr<BeforeUnloadEvent> event = new BeforeUnloadEvent;
3384     event->setTarget(doc.get());
3385     doc->handleWindowEvent(event.get(), false);
3386
3387     if (!event->defaultPrevented() && doc)
3388         doc->defaultEventHandler(event.get());
3389     if (event->result().isNull())
3390         return true;
3391
3392     DeprecatedString text = event->result().deprecatedString();
3393     text.replace(QChar('\\'), backslashAsCurrencySymbol());
3394
3395     return [_bridge runBeforeUnloadConfirmPanelWithMessage:text.getNSString()];
3396
3397     END_BLOCK_OBJC_EXCEPTIONS;
3398
3399     return true;
3400 }
3401
3402 void FrameMac::dragSourceMovedTo(const PlatformMouseEvent& event)
3403 {
3404     if (_dragSrc && _dragSrcMayBeDHTML) {
3405         // for now we don't care if event handler cancels default behavior, since there is none
3406         dispatchDragSrcEvent(dragEvent, event);
3407     }
3408 }
3409
3410 void FrameMac::dragSourceEndedAt(const PlatformMouseEvent& event, NSDragOperation operation)
3411 {
3412     if (_dragSrc && _dragSrcMayBeDHTML) {
3413         _dragClipboard->setDestinationOperation(operation);
3414         // for now we don't care if event handler cancels default behavior, since there is none
3415         dispatchDragSrcEvent(dragendEvent, event);
3416     }
3417     freeClipboard();
3418     _dragSrc = 0;
3419 }
3420
3421 // returns if we should continue "default processing", i.e., whether eventhandler canceled
3422 bool FrameMac::dispatchDragSrcEvent(const AtomicString &eventType, const PlatformMouseEvent& event) const
3423 {
3424     bool noDefaultProc = d->m_view->dispatchDragEvent(eventType, _dragSrc.get(), event, _dragClipboard.get());
3425     return !noDefaultProc;
3426 }
3427
3428 void FrameMac::detachFromView()
3429 {
3430     setView(0);
3431 }
3432
3433 }