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