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