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