2006-03-28 Eric Seidel <eseidel@apple.com>
[WebKit-https.git] / WebCore / bridge / mac / FrameMac.mm
1 /*
2  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "config.h"
27 #import "FrameMac.h"
28
29 #import "BrowserExtensionMac.h"
30 #import "Cache.h"
31 #import "Cursor.h"
32 #import "DOMInternal.h"
33 #import "EventNames.h"
34 #import "FloatRect.h"
35 #import "FoundationExtras.h"
36 #import "FramePrivate.h"
37 #import "FrameView.h"
38 #import "GraphicsContext.h"
39 #import "HTMLFormElement.h"
40 #import "HTMLGenericFormElement.h"
41 #import "InlineTextBox.h"
42 #import "AccessibilityObjectCache.h"
43 #import "ClipboardMac.h"
44 #import "KWQEditCommand.h"
45 #import "BlockExceptions.h"
46 #import "KWQFormData.h"
47 #import "TransferJob.h"
48 #import "Logging.h"
49 #import "KWQPageState.h"
50 #import "RegularExpression.h"
51 #import "KWQScrollBar.h"
52 #import "TextEncoding.h"
53 #import "PlatformKeyboardEvent.h"
54 #import "PlatformMouseEvent.h"
55 #import "MouseEventWithHitTestResults.h"
56 #import "Plugin.h"
57 #import "RenderTableCell.h"
58 #import "SelectionController.h"
59 #import "VisiblePosition.h"
60 #import "WebCoreFrameBridge.h"
61 #import "WebCoreGraphicsBridge.h"
62 #import "WebCoreViewFactory.h"
63 #import "WebDashboardRegion.h"
64 #import "PlatformWheelEvent.h"
65 #import "CSSComputedStyleDeclaration.h"
66 #import "csshelper.h"
67 #import "dom2_eventsimpl.h"
68 #import "Range.h"
69 #import "Position.h"
70 #import "HTMLDocument.h"
71 #import "html_tableimpl.h"
72 #import "kjs_binding.h"
73 #import "kjs_window.h"
74 #import "RenderCanvas.h"
75 #import "render_frames.h"
76 #import "RenderImage.h"
77 #import "render_list.h"
78 #import "render_style.h"
79 #import "RenderTheme.h"
80 #import "TextIterator.h"
81 #import "visible_units.h"
82 #import <JavaScriptCore/NP_jsobject.h>
83 #import <JavaScriptCore/WebScriptObjectPrivate.h>
84 #import <JavaScriptCore/identifier.h>
85 #import <JavaScriptCore/interpreter.h>
86 #import <JavaScriptCore/npruntime_impl.h>
87 #import <JavaScriptCore/property_map.h>
88 #import <JavaScriptCore/runtime.h>
89 #import <JavaScriptCore/runtime_root.h>
90
91 #undef _KWQ_TIMING
92
93 @interface NSObject (WebPlugIn)
94 - (id)objectForWebScript;
95 - (void *)pluginScriptableObject;
96 @end
97
98 using namespace KJS;
99 using namespace Bindings;
100
101 using namespace KIO;
102
103 namespace WebCore {
104
105 using namespace EventNames;
106 using namespace HTMLNames;
107
108 NSEvent *FrameMac::_currentEvent = nil;
109
110 bool FrameView::isFrameView() const
111 {
112     return true;
113 }
114
115 FrameMac::FrameMac(Page* page, RenderPart* ownerRenderer)
116     : Frame(page, ownerRenderer)
117     , _bridge(nil)
118     , _mouseDownView(nil)
119     , _sendingEventToSubview(false)
120     , _mouseDownMayStartDrag(false)
121     , _mouseDownMayStartSelect(false)
122     , _activationEventNumber(0)
123     , _formValuesAboutToBeSubmitted(nil)
124     , _formAboutToBeSubmitted(nil)
125     , _bindingRoot(0)
126     , _windowScriptObject(0)
127     , _windowScriptNPObject(0)
128 {
129     mutableInstances().prepend(this);
130
131     d->m_extension = new BrowserExtensionMac(this);
132 }
133
134 FrameMac::~FrameMac()
135 {
136     setView(0);
137     mutableInstances().remove(this);
138     freeClipboard();
139     clearRecordedFormValues();    
140     
141     [_bridge clearFrame];
142     KWQRelease(_bridge);
143     _bridge = nil;
144 }
145
146 void FrameMac::freeClipboard()
147 {
148     if (_dragClipboard)
149         _dragClipboard->setAccessPolicy(ClipboardMac::Numb);
150 }
151
152 bool FrameMac::openURL(const KURL &url)
153 {
154     BEGIN_BLOCK_OBJC_EXCEPTIONS;
155
156     // FIXME: The lack of args here to get the reload flag from
157     // indicates a problem in how we use Frame::processObjectRequest,
158     // where we are opening the URL before the args are set up.
159     [_bridge loadURL:url.getNSURL()
160             referrer:[_bridge referrer]
161               reload:NO
162          userGesture:userGestureHint()
163               target:nil
164      triggeringEvent:nil
165                 form:nil
166           formValues:nil];
167
168     END_BLOCK_OBJC_EXCEPTIONS;
169
170     return true;
171 }
172
173 void FrameMac::openURLRequest(const ResourceRequest& request)
174 {
175     BEGIN_BLOCK_OBJC_EXCEPTIONS;
176
177     NSString *referrer;
178     String argsReferrer = request.referrer();
179     if (!argsReferrer.isEmpty())
180         referrer = argsReferrer;
181     else
182         referrer = [_bridge referrer];
183
184     [_bridge loadURL:request.url().getNSURL()
185             referrer:referrer
186               reload:request.reload
187          userGesture:userGestureHint()
188               target:request.frameName
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, (int)point.x, (int)point.y);    
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     int mouseDownX, mouseDownY;
1529     d->m_view->viewportToContents((int)loc.x, (int)loc.y, mouseDownX, mouseDownY);
1530     RenderObject::NodeInfo nodeInfo(true, false);
1531     renderer()->layer()->hitTest(nodeInfo, mouseDownX, mouseDownY);
1532     bool srcIsDHTML;
1533     return nodeInfo.innerNode()->renderer()->draggableNode(DHTMLFlag, UAFlag, mouseDownX, mouseDownY, srcIsDHTML);
1534 }
1535
1536 // The link drag hysteresis is much larger than the others because there
1537 // needs to be enough space to cancel the link press without starting a link drag,
1538 // and because dragging links is rare.
1539 const float LinkDragHysteresis = 40.0;
1540 const float ImageDragHysteresis = 5.0;
1541 const float TextDragHysteresis = 3.0;
1542 const float GeneralDragHysteresis = 3.0;
1543 const float TextDragDelay = 0.15;
1544
1545 bool FrameMac::dragHysteresisExceeded(float dragLocationX, float dragLocationY) const
1546 {
1547     int dragX, dragY;
1548     d->m_view->viewportToContents((int)dragLocationX, (int)dragLocationY, dragX, dragY);
1549     float deltaX = fabsf(dragX - _mouseDownX);
1550     float deltaY = fabsf(dragY - _mouseDownY);
1551     
1552     float threshold = GeneralDragHysteresis;
1553     if (_dragSrcIsImage) {
1554         threshold = ImageDragHysteresis;
1555     } else if (_dragSrcIsLink) {
1556         threshold = LinkDragHysteresis;
1557     } else if (_dragSrcInSelection) {
1558         threshold = TextDragHysteresis;
1559     }
1560     return deltaX >= threshold || deltaY >= threshold;
1561 }
1562
1563 void FrameMac::handleMouseMoveEvent(const MouseEventWithHitTestResults& event)
1564 {
1565     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1566
1567     if ([_currentEvent type] == NSLeftMouseDragged) {
1568         NSView *view = mouseDownViewIfStillGood();
1569
1570         if (view) {
1571             _sendingEventToSubview = true;
1572             [view mouseDragged:_currentEvent];
1573             _sendingEventToSubview = false;
1574             return;
1575         }
1576
1577         // Careful that the drag starting logic stays in sync with eventMayStartDrag()
1578     
1579         if (_mouseDownMayStartDrag && !_dragSrc) {
1580             BOOL tempFlag1, tempFlag2;
1581             [_bridge allowDHTMLDrag:&tempFlag1 UADrag:&tempFlag2];
1582             _dragSrcMayBeDHTML = tempFlag1;
1583             _dragSrcMayBeUA = tempFlag2;
1584             if (!_dragSrcMayBeDHTML && !_dragSrcMayBeUA) {
1585                 _mouseDownMayStartDrag = false;     // no element is draggable
1586             }
1587         }
1588         
1589         if (_mouseDownMayStartDrag && !_dragSrc) {
1590             // try to find an element that wants to be dragged
1591             RenderObject::NodeInfo nodeInfo(true, false);
1592             renderer()->layer()->hitTest(nodeInfo, _mouseDownX, _mouseDownY);
1593             Node *node = nodeInfo.innerNode();
1594             _dragSrc = (node && node->renderer()) ? node->renderer()->draggableNode(_dragSrcMayBeDHTML, _dragSrcMayBeUA, _mouseDownX, _mouseDownY, _dragSrcIsDHTML) : 0;
1595             if (!_dragSrc) {
1596                 _mouseDownMayStartDrag = false;     // no element is draggable
1597             } else {
1598                 // remember some facts about this source, while we have a NodeInfo handy
1599                 node = nodeInfo.URLElement();
1600                 _dragSrcIsLink = node && node->isLink();
1601
1602                 node = nodeInfo.innerNonSharedNode();
1603                 _dragSrcIsImage = node && node->renderer() && node->renderer()->isImage();
1604                 
1605                 _dragSrcInSelection = isPointInsideSelection(_mouseDownX, _mouseDownY);
1606             }                
1607         }
1608         
1609         // For drags starting in the selection, the user must wait between the mousedown and mousedrag,
1610         // or else we bail on the dragging stuff and allow selection to occur
1611         if (_mouseDownMayStartDrag && _dragSrcInSelection && [_currentEvent timestamp] - _mouseDownTimestamp < TextDragDelay) {
1612             _mouseDownMayStartDrag = false;
1613             // ...but if this was the first click in the window, we don't even want to start selection
1614             if (_activationEventNumber == [_currentEvent eventNumber]) {
1615                 _mouseDownMayStartSelect = false;
1616             }
1617         }
1618
1619         if (_mouseDownMayStartDrag) {
1620             // We are starting a text/image/url drag, so the cursor should be an arrow
1621             d->m_view->setCursor(pointerCursor());
1622             
1623             NSPoint dragLocation = [_currentEvent locationInWindow];
1624             if (dragHysteresisExceeded(dragLocation.x, dragLocation.y)) {
1625                 
1626                 // Once we're past the hysteresis point, we don't want to treat this gesture as a click
1627                 d->m_view->invalidateClick();
1628
1629                 NSImage *dragImage = nil;       // we use these values if WC is out of the loop
1630                 NSPoint dragLoc = NSZeroPoint;
1631                 NSDragOperation srcOp = NSDragOperationNone;                
1632                 BOOL wcWrotePasteboard = NO;
1633                 if (_dragSrcMayBeDHTML) {
1634                     NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
1635                     // Must be done before ondragstart adds types and data to the pboard,
1636                     // also done for security, as it erases data from the last drag
1637                     [pasteboard declareTypes:[NSArray array] owner:nil];
1638                     
1639                     freeClipboard();    // would only happen if we missed a dragEnd.  Do it anyway, just
1640                                         // to make sure it gets numbified
1641                     _dragClipboard = new ClipboardMac(true, pasteboard, ClipboardMac::Writable, this);
1642                     
1643                     // If this is drag of an element, get set up to generate a default image.  Otherwise
1644                     // WebKit will generate the default, the element doesn't override.
1645                     if (_dragSrcIsDHTML) {
1646                         int srcX, srcY;
1647                         _dragSrc->renderer()->absolutePosition(srcX, srcY);
1648                         _dragClipboard->setDragImageElement(_dragSrc.get(), IntPoint(_mouseDownX - srcX, _mouseDownY - srcY));
1649                     } 
1650
1651                     _mouseDownMayStartDrag = dispatchDragSrcEvent(dragstartEvent, m_mouseDown);
1652                     // Invalidate clipboard here against anymore pasteboard writing for security.  The drag
1653                     // image can still be changed as we drag, but not the pasteboard data.
1654                     _dragClipboard->setAccessPolicy(ClipboardMac::ImageWritable);
1655                     
1656                     if (_mouseDownMayStartDrag) {
1657                         // gather values from DHTML element, if it set any
1658                         _dragClipboard->sourceOperation(&srcOp);
1659
1660                         NSArray *types = [pasteboard types];
1661                         wcWrotePasteboard = types && [types count] > 0;
1662
1663                         if (_dragSrcMayBeDHTML) {
1664                             dragImage = _dragClipboard->dragNSImage(&dragLoc);
1665                         }
1666                         
1667                         // Yuck, dragSourceMovedTo() can be called as a result of kicking off the drag with
1668                         // dragImage!  Because of that dumb reentrancy, we may think we've not started the
1669                         // drag when that happens.  So we have to assume it's started before we kick it off.
1670                         _dragClipboard->setDragHasStarted();
1671                     }
1672                 }
1673                 
1674                 if (_mouseDownMayStartDrag) {
1675                     BOOL startedDrag = [_bridge startDraggingImage:dragImage at:dragLoc operation:srcOp event:_currentEvent sourceIsDHTML:_dragSrcIsDHTML DHTMLWroteData:wcWrotePasteboard];
1676                     if (!startedDrag && _dragSrcMayBeDHTML) {
1677                         // WebKit canned the drag at the last minute - we owe _dragSrc a DRAGEND event
1678                         PlatformMouseEvent event;
1679                         dispatchDragSrcEvent(dragendEvent, event);
1680                         _mouseDownMayStartDrag = false;
1681                     }
1682                 } 
1683
1684                 if (!_mouseDownMayStartDrag) {
1685                     // something failed to start the drag, cleanup
1686                     freeClipboard();
1687                     _dragSrc = 0;
1688                 }
1689             }
1690
1691             // No more default handling (like selection), whether we're past the hysteresis bounds or not
1692             return;
1693         }
1694         if (!_mouseDownMayStartSelect) {
1695             return;
1696         }
1697
1698         // Don't allow dragging or click handling after we've started selecting.
1699         _mouseDownMayStartDrag = false;
1700         d->m_view->invalidateClick();
1701
1702         Node* node = event.innerNode();
1703         RenderLayer* layer = 0;
1704         if (node && node->renderer())
1705             layer = node->renderer()->enclosingLayer();
1706             
1707         // If the selection is contained in a layer that can scroll, that layer should handle the autoscroll
1708         // Otherwise, let the bridge handle it so the view can scroll itself.
1709         while (layer && !layer->shouldAutoscroll())
1710             layer = layer->parent();
1711         if (layer)
1712             handleAutoscroll(layer);
1713         else
1714             [_bridge handleAutoscrollForMouseDragged:_currentEvent];
1715             
1716     } else {
1717         // If we allowed the other side of the bridge to handle a drag
1718         // last time, then m_bMousePressed might still be set. So we
1719         // clear it now to make sure the next move after a drag
1720         // doesn't look like a drag.
1721         d->m_bMousePressed = false;
1722     }
1723
1724     Frame::handleMouseMoveEvent(event);
1725
1726     END_BLOCK_OBJC_EXCEPTIONS;
1727 }
1728
1729 // Returns whether caller should continue with "the default processing", which is the same as 
1730 // the event handler NOT setting the return value to false
1731 bool FrameMac::dispatchCPPEvent(const AtomicString &eventType, ClipboardMac::AccessPolicy policy)
1732 {
1733     Node* target = d->m_selection.start().element();
1734     if (!target && document())
1735         target = document()->body();
1736     if (!target)
1737         return true;
1738
1739     RefPtr<ClipboardMac> clipboard = new ClipboardMac(false, [NSPasteboard generalPasteboard], (ClipboardMac::AccessPolicy)policy);
1740
1741     ExceptionCode ec = 0;
1742     RefPtr<Event> evt = new ClipboardEvent(eventType, true, true, clipboard.get());
1743     EventTargetNodeCast(target)->dispatchEvent(evt, ec, true);
1744     bool noDefaultProcessing = evt->defaultPrevented();
1745
1746     // invalidate clipboard here for security
1747     clipboard->setAccessPolicy(ClipboardMac::Numb);
1748
1749     return !noDefaultProcessing;
1750 }
1751
1752 // WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items.  They
1753 // also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items.
1754 // We need to use onbeforecopy as a real menu enabler because we allow elements that are not
1755 // normally selectable to implement copy/paste (like divs, or a document body).
1756
1757 bool FrameMac::mayCut()
1758 {
1759     return !dispatchCPPEvent(beforecutEvent, ClipboardMac::Numb);
1760 }
1761
1762 bool FrameMac::mayCopy()
1763 {
1764     return !dispatchCPPEvent(beforecopyEvent, ClipboardMac::Numb);
1765 }
1766
1767 bool FrameMac::mayPaste()
1768 {
1769     return !dispatchCPPEvent(beforepasteEvent, ClipboardMac::Numb);
1770 }
1771
1772 bool FrameMac::tryCut()
1773 {
1774     // Must be done before oncut adds types and data to the pboard,
1775     // also done for security, as it erases data from the last copy/paste.
1776     [[NSPasteboard generalPasteboard] declareTypes:[NSArray array] owner:nil];
1777
1778     return !dispatchCPPEvent(cutEvent, ClipboardMac::Writable);
1779 }
1780
1781 bool FrameMac::tryCopy()
1782 {
1783     // Must be done before oncopy adds types and data to the pboard,
1784     // also done for security, as it erases data from the last copy/paste.
1785     [[NSPasteboard generalPasteboard] declareTypes:[NSArray array] owner:nil];
1786
1787     return !dispatchCPPEvent(copyEvent, ClipboardMac::Writable);
1788 }
1789
1790 bool FrameMac::tryPaste()
1791 {
1792     return !dispatchCPPEvent(pasteEvent, ClipboardMac::Readable);
1793 }
1794
1795 void FrameMac::handleMouseReleaseEvent(const MouseEventWithHitTestResults& event)
1796 {
1797     NSView *view = mouseDownViewIfStillGood();
1798     if (!view) {
1799         // If this was the first click in the window, we don't even want to clear the selection.
1800         // This case occurs when the user clicks on a draggable element, since we have to process
1801         // the mouse down and drag events to see if we might start a drag.  For other first clicks
1802         // in a window, we just don't acceptFirstMouse, and the whole down-drag-up sequence gets
1803         // ignored upstream of this layer.
1804         if (_activationEventNumber != [_currentEvent eventNumber])
1805             Frame::handleMouseReleaseEvent(event);
1806         return;
1807     }
1808     stopAutoscrollTimer();
1809     
1810     _sendingEventToSubview = true;
1811     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1812     [view mouseUp:_currentEvent];
1813     END_BLOCK_OBJC_EXCEPTIONS;
1814     _sendingEventToSubview = false;
1815 }
1816
1817 bool FrameMac::passSubframeEventToSubframe(MouseEventWithHitTestResults& event, Frame* subframePart)
1818 {
1819     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1820
1821     switch ([_currentEvent type]) {
1822         case NSMouseMoved: {
1823             ASSERT(subframePart);
1824             [Mac(subframePart)->bridge() mouseMoved:_currentEvent];
1825             return true;
1826         }
1827         
1828         case NSLeftMouseDown: {
1829             Node *node = event.innerNode();
1830             if (!node) {
1831                 return false;
1832             }
1833             RenderObject *renderer = node->renderer();
1834             if (!renderer || !renderer->isWidget()) {
1835                 return false;
1836             }
1837             Widget *widget = static_cast<RenderWidget *>(renderer)->widget();
1838             if (!widget || !widget->isFrameView())
1839                 return false;
1840             if (!passWidgetMouseDownEventToWidget(static_cast<RenderWidget *>(renderer))) {
1841                 return false;
1842             }
1843             _mouseDownWasInSubframe = true;
1844             return true;
1845         }
1846         case NSLeftMouseUp: {
1847             if (!_mouseDownWasInSubframe) {
1848                 return false;
1849             }
1850             NSView *view = mouseDownViewIfStillGood();
1851             if (!view) {
1852                 return false;
1853             }
1854             ASSERT(!_sendingEventToSubview);
1855             _sendingEventToSubview = true;
1856             [view mouseUp:_currentEvent];
1857             _sendingEventToSubview = false;
1858             return true;
1859         }
1860         case NSLeftMouseDragged: {
1861             if (!_mouseDownWasInSubframe) {
1862                 return false;
1863             }
1864             NSView *view = mouseDownViewIfStillGood();
1865             if (!view) {
1866                 return false;
1867             }
1868             ASSERT(!_sendingEventToSubview);
1869             _sendingEventToSubview = true;
1870             [view mouseDragged:_currentEvent];
1871             _sendingEventToSubview = false;
1872             return true;
1873         }
1874         default:
1875             return false;
1876     }
1877     END_BLOCK_OBJC_EXCEPTIONS;
1878
1879     return false;
1880 }
1881
1882 bool FrameMac::passWheelEventToChildWidget(Node *node)
1883 {
1884     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1885         
1886     if ([_currentEvent type] != NSScrollWheel || _sendingEventToSubview || !node) 
1887         return false;
1888     else {
1889         RenderObject *renderer = node->renderer();
1890         if (!renderer || !renderer->isWidget())
1891             return false;
1892         Widget *widget = static_cast<RenderWidget *>(renderer)->widget();
1893         if (!widget)
1894             return false;
1895             
1896         NSView *nodeView = widget->getView();
1897         ASSERT(nodeView);
1898         ASSERT([nodeView superview]);
1899         NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[_currentEvent locationInWindow] fromView:nil]];
1900     
1901         ASSERT(view);
1902         _sendingEventToSubview = true;
1903         [view scrollWheel:_currentEvent];
1904         _sendingEventToSubview = false;
1905         return true;
1906     }
1907             
1908     END_BLOCK_OBJC_EXCEPTIONS;
1909     return false;
1910 }
1911
1912 void FrameMac::mouseDown(NSEvent *event)
1913 {
1914     FrameView *v = d->m_view.get();
1915     if (!v || _sendingEventToSubview)
1916         return;
1917
1918     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1919
1920     prepareForUserAction();
1921
1922     _mouseDownView = nil;
1923     _dragSrc = 0;
1924     
1925     NSEvent *oldCurrentEvent = _currentEvent;
1926     _currentEvent = KWQRetain(event);
1927     m_mouseDown = PlatformMouseEvent(event);
1928     NSPoint loc = [event locationInWindow];
1929     d->m_view->viewportToContents((int)loc.x, (int)loc.y, _mouseDownX, _mouseDownY);
1930     _mouseDownTimestamp = [event timestamp];
1931
1932     _mouseDownMayStartDrag = false;
1933     _mouseDownMayStartSelect = false;
1934
1935     v->handleMousePressEvent(event);
1936     
1937     ASSERT(_currentEvent == event);
1938     KWQRelease(event);
1939     _currentEvent = oldCurrentEvent;
1940
1941     END_BLOCK_OBJC_EXCEPTIONS;
1942 }
1943
1944 void FrameMac::mouseDragged(NSEvent *event)
1945 {
1946     FrameView *v = d->m_view.get();
1947     if (!v || _sendingEventToSubview) {
1948         return;
1949     }
1950
1951     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1952
1953     NSEvent *oldCurrentEvent = _currentEvent;
1954     _currentEvent = KWQRetain(event);
1955
1956     v->handleMouseMoveEvent(event);
1957     
1958     ASSERT(_currentEvent == event);
1959     KWQRelease(event);
1960     _currentEvent = oldCurrentEvent;
1961
1962     END_BLOCK_OBJC_EXCEPTIONS;
1963 }
1964
1965 void FrameMac::mouseUp(NSEvent *event)
1966 {
1967     FrameView *v = d->m_view.get();
1968     if (!v || _sendingEventToSubview)
1969         return;
1970
1971     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1972
1973     NSEvent *oldCurrentEvent = _currentEvent;
1974     _currentEvent = KWQRetain(event);
1975
1976     // Our behavior here is a little different that Qt. Qt always sends
1977     // a mouse release event, even for a double click. To correct problems
1978     // in khtml's DOM click event handling we do not send a release here
1979     // for a double click. Instead we send that event from FrameView's
1980     // handleMouseDoubleClickEvent. Note also that the third click of
1981     // a triple click is treated as a single click, but the fourth is then
1982     // treated as another double click. Hence the "% 2" below.
1983     int clickCount = [event clickCount];
1984     if (clickCount > 0 && clickCount % 2 == 0)
1985         v->handleMouseDoubleClickEvent(event);
1986     else
1987         v->handleMouseReleaseEvent(event);
1988     
1989     ASSERT(_currentEvent == event);
1990     KWQRelease(event);
1991     _currentEvent = oldCurrentEvent;
1992     
1993     _mouseDownView = nil;
1994
1995     END_BLOCK_OBJC_EXCEPTIONS;
1996 }
1997
1998 /*
1999  A hack for the benefit of AK's PopUpButton, which uses the Carbon menu manager, which thus
2000  eats all subsequent events after it is starts its modal tracking loop.  After the interaction
2001  is done, this routine is used to fix things up.  When a mouse down started us tracking in
2002  the widget, we post a fake mouse up to balance the mouse down we started with. When a 
2003  key down started us tracking in the widget, we post a fake key up to balance things out.
2004  In addition, we post a fake mouseMoved to get the cursor in sync with whatever we happen to 
2005  be over after the tracking is done.
2006  */
2007 void FrameMac::sendFakeEventsAfterWidgetTracking(NSEvent *initiatingEvent)
2008 {
2009     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2010
2011     _sendingEventToSubview = false;
2012     int eventType = [initiatingEvent type];
2013     if (eventType == NSLeftMouseDown || eventType == NSKeyDown) {
2014         NSEvent *fakeEvent = nil;
2015         if (eventType == NSLeftMouseDown) {
2016             fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
2017                                     location:[initiatingEvent locationInWindow]
2018                                 modifierFlags:[initiatingEvent modifierFlags]
2019                                     timestamp:[initiatingEvent timestamp]
2020                                 windowNumber:[initiatingEvent windowNumber]
2021                                         context:[initiatingEvent context]
2022                                     eventNumber:[initiatingEvent eventNumber]
2023                                     clickCount:[initiatingEvent clickCount]
2024                                     pressure:[initiatingEvent pressure]];
2025         
2026             mouseUp(fakeEvent);
2027         }
2028         else { // eventType == NSKeyDown
2029             fakeEvent = [NSEvent keyEventWithType:NSKeyUp
2030                                     location:[initiatingEvent locationInWindow]
2031                                modifierFlags:[initiatingEvent modifierFlags]
2032                                    timestamp:[initiatingEvent timestamp]
2033                                 windowNumber:[initiatingEvent windowNumber]
2034                                      context:[initiatingEvent context]
2035                                   characters:[initiatingEvent characters] 
2036                  charactersIgnoringModifiers:[initiatingEvent charactersIgnoringModifiers] 
2037                                    isARepeat:[initiatingEvent isARepeat] 
2038                                      keyCode:[initiatingEvent keyCode]];
2039             keyEvent(fakeEvent);
2040         }
2041         // FIXME:  We should really get the current modifierFlags here, but there's no way to poll
2042         // them in Cocoa, and because the event stream was stolen by the Carbon menu code we have
2043         // no up-to-date cache of them anywhere.
2044         fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
2045                                        location:[[_bridge window] convertScreenToBase:[NSEvent mouseLocation]]
2046                                   modifierFlags:[initiatingEvent modifierFlags]
2047                                       timestamp:[initiatingEvent timestamp]
2048                                    windowNumber:[initiatingEvent windowNumber]
2049                                         context:[initiatingEvent context]
2050                                     eventNumber:0
2051                                      clickCount:0
2052                                        pressure:0];
2053         mouseMoved(fakeEvent);
2054     }
2055     
2056     END_BLOCK_OBJC_EXCEPTIONS;
2057 }
2058
2059 void FrameMac::mouseMoved(NSEvent *event)
2060 {
2061     FrameView *v = d->m_view.get();
2062     // Reject a mouse moved if the button is down - screws up tracking during autoscroll
2063     // These happen because WebKit sometimes has to fake up moved events.
2064     if (!v || d->m_bMousePressed || _sendingEventToSubview)
2065         return;
2066     
2067     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2068
2069     NSEvent *oldCurrentEvent = _currentEvent;
2070     _currentEvent = KWQRetain(event);
2071     
2072     v->handleMouseMoveEvent(event);
2073     
2074     ASSERT(_currentEvent == event);
2075     KWQRelease(event);
2076     _currentEvent = oldCurrentEvent;
2077
2078     END_BLOCK_OBJC_EXCEPTIONS;
2079 }
2080
2081 // Called as we walk up the element chain for nodes with CSS property -khtml-user-drag == auto
2082 bool FrameMac::shouldDragAutoNode(Node* node, int x, int y) const
2083 {
2084     // We assume that WebKit only cares about dragging things that can be leaf nodes (text, images, urls).
2085     // This saves a bunch of expensive calls (creating WC and WK element dicts) as we walk farther up
2086     // the node hierarchy, and we also don't have to cook up a way to ask WK about non-leaf nodes
2087     // (since right now WK just hit-tests using a cached lastMouseDown).
2088     if (!node->hasChildNodes() && d->m_view) {
2089         int windowX, windowY;
2090         d->m_view->contentsToViewport(x, y, windowX, windowY);
2091         NSPoint eventLoc = {windowX, windowY};
2092         return [_bridge mayStartDragAtEventLocation:eventLoc];
2093     } else
2094         return NO;
2095 }
2096
2097 bool FrameMac::sendContextMenuEvent(NSEvent *event)
2098 {
2099     Document* doc = d->m_doc.get();
2100     FrameView* v = d->m_view.get();
2101     if (!doc || !v)
2102         return false;
2103
2104     bool swallowEvent;
2105     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2106
2107     NSEvent *oldCurrentEvent = _currentEvent;
2108     _currentEvent = KWQRetain(event);
2109     
2110     PlatformMouseEvent mouseEvent(event);
2111
2112     int xm, ym;
2113     v->viewportToContents(mouseEvent.x(), mouseEvent.y(), xm, ym);
2114
2115     MouseEventWithHitTestResults mev = doc->prepareMouseEvent(false, true, false, xm, ym, mouseEvent);
2116
2117     swallowEvent = v->dispatchMouseEvent(contextmenuEvent, mev.innerNode(), true, 0, mouseEvent, true);
2118     if (!swallowEvent && !isPointInsideSelection(xm, ym) &&
2119         ([_bridge selectWordBeforeMenuEvent] || [_bridge isEditable] || mev.innerNode()->isContentEditable())) {
2120         selectClosestWordFromMouseEvent(mouseEvent, mev.innerNode(), xm, ym);
2121     }
2122
2123     ASSERT(_currentEvent == event);
2124     KWQRelease(event);
2125     _currentEvent = oldCurrentEvent;
2126
2127     return swallowEvent;
2128
2129     END_BLOCK_OBJC_EXCEPTIONS;
2130
2131     return false;
2132 }
2133
2134 struct ListItemInfo {
2135     unsigned start;
2136     unsigned end;
2137 };
2138
2139 NSFileWrapper *FrameMac::fileWrapperForElement(Element *e)
2140 {
2141     NSFileWrapper *wrapper = nil;
2142     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2143     
2144     const AtomicString& attr = e->getAttribute(srcAttr);
2145     if (!attr.isEmpty()) {
2146         NSURL *URL = completeURL(attr.deprecatedString()).getNSURL();
2147         wrapper = [_bridge fileWrapperForURL:URL];
2148     }    
2149     if (!wrapper) {
2150         RenderImage *renderer = static_cast<RenderImage *>(e->renderer());
2151         if (renderer->cachedImage() && !renderer->cachedImage()->isErrorImage()) {
2152             wrapper = [[NSFileWrapper alloc] initRegularFileWithContents:(NSData*)(renderer->cachedImage()->image()->getTIFFRepresentation())];
2153             [wrapper setPreferredFilename:@"image.tiff"];
2154             [wrapper autorelease];
2155         }
2156     }
2157
2158     return wrapper;
2159
2160     END_BLOCK_OBJC_EXCEPTIONS;
2161
2162     return nil;
2163 }
2164
2165 static Element *listParent(Element *item)
2166 {
2167     while (!item->hasTagName(ulTag) && !item->hasTagName(olTag)) {
2168         item = static_cast<Element *>(item->parentNode());
2169         if (!item)
2170             break;
2171     }
2172     return item;
2173 }
2174
2175 static Node* isTextFirstInListItem(Node *e)
2176 {
2177     if (!e->isTextNode())
2178         return 0;
2179     Node* par = e->parentNode();
2180     while (par) {
2181         if (par->firstChild() != e)
2182             return 0;
2183         if (par->hasTagName(liTag))
2184             return par;
2185         e = par;
2186         par = par->parentNode();
2187     }
2188     return 0;
2189 }
2190
2191 // FIXME: This collosal function needs to be refactored into maintainable smaller bits.
2192
2193 #define BULLET_CHAR 0x2022
2194 #define SQUARE_CHAR 0x25AA
2195 #define CIRCLE_CHAR 0x25E6
2196
2197 NSAttributedString *FrameMac::attributedString(Node *_start, int startOffset, Node *endNode, int endOffset)
2198 {
2199     ListItemInfo info;
2200     NSMutableAttributedString *result;
2201     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2202
2203     Node * _startNode = _start;
2204
2205     if (!_startNode)
2206         return nil;
2207
2208     result = [[[NSMutableAttributedString alloc] init] autorelease];
2209
2210     bool hasNewLine = true;
2211     bool addedSpace = true;
2212     NSAttributedString *pendingStyledSpace = nil;
2213     bool hasParagraphBreak = true;
2214     const Element *linkStartNode = 0;
2215     unsigned linkStartLocation = 0;
2216     DeprecatedPtrList<Element> listItems;
2217     DeprecatedValueList<ListItemInfo> listItemLocations;
2218     float maxMarkerWidth = 0;
2219     
2220     Node *n = _startNode;
2221     
2222     // If the first item is the entire text of a list item, use the list item node as the start of the 
2223     // selection, not the text node.  The user's intent was probably to select the list.
2224     if (n->isTextNode() && startOffset == 0) {
2225         Node *startListNode = isTextFirstInListItem(_startNode);
2226         if (startListNode){
2227             _startNode = startListNode;
2228             n = _startNode;
2229         }
2230     }
2231     
2232     while (n) {
2233         RenderObject *renderer = n->renderer();
2234         if (renderer) {
2235             RenderStyle *style = renderer->style();
2236             NSFont *font = style->font().getNSFont();
2237             bool needSpace = pendingStyledSpace != nil;
2238             if (n->isTextNode()) {
2239                 if (hasNewLine) {
2240                     addedSpace = true;
2241                     needSpace = false;
2242                     [pendingStyledSpace release];
2243                     pendingStyledSpace = nil;
2244                     hasNewLine = false;
2245                 }
2246                 DeprecatedString text;
2247                 DeprecatedString str = n->nodeValue().deprecatedString();
2248                 int start = (n == _startNode) ? startOffset : -1;
2249                 int end = (n == endNode) ? endOffset : -1;
2250                 if (renderer->isText()) {
2251                     if (!style->collapseWhiteSpace()) {
2252                         if (needSpace && !addedSpace) {
2253                             if (text.isEmpty() && linkStartLocation == [result length])
2254                                 ++linkStartLocation;
2255                             [result appendAttributedString:pendingStyledSpace];
2256                         }
2257                         int runStart = (start == -1) ? 0 : start;
2258                         int runEnd = (end == -1) ? str.length() : end;
2259                         text += str.mid(runStart, runEnd-runStart);
2260                         [pendingStyledSpace release];
2261                         pendingStyledSpace = nil;
2262                         addedSpace = str[runEnd-1].direction() == QChar::DirWS;
2263                     }
2264                     else {
2265                         RenderText* textObj = static_cast<RenderText*>(renderer);
2266                         if (!textObj->firstTextBox() && str.length() > 0 && !addedSpace) {
2267                             // We have no runs, but we do have a length.  This means we must be
2268                             // whitespace that collapsed away at the end of a line.
2269                             text += ' ';
2270                             addedSpace = true;
2271                         }
2272                         else {
2273                             addedSpace = false;
2274                             for (InlineTextBox* box = textObj->firstTextBox(); box; box = box->nextTextBox()) {
2275                                 int runStart = (start == -1) ? box->m_start : start;
2276                                 int runEnd = (end == -1) ? box->m_start + box->m_len : end;
2277                                 runEnd = kMin(runEnd, box->m_start + box->m_len);
2278                                 if (runStart >= box->m_start &&
2279                                     runStart < box->m_start + box->m_len) {
2280                                     if (box == textObj->firstTextBox() && box->m_start == runStart && runStart > 0)
2281                                         needSpace = true; // collapsed space at the start
2282                                     if (needSpace && !addedSpace) {
2283                                         if (pendingStyledSpace != nil) {
2284                                             if (text.isEmpty() && linkStartLocation == [result length])
2285                                                 ++linkStartLocation;
2286                                             [result appendAttributedString:pendingStyledSpace];
2287                                         } else
2288                                             text += ' ';
2289                                     }
2290                                     DeprecatedString runText = str.mid(runStart, runEnd - runStart);
2291                                     runText.replace('\n', ' ');
2292                                     text += runText;
2293                                     int nextRunStart = box->nextTextBox() ? box->nextTextBox()->m_start : str.length(); // collapsed space between runs or at the end
2294                                     needSpace = nextRunStart > runEnd;
2295                                     [pendingStyledSpace release];
2296                                     pendingStyledSpace = nil;
2297                                     addedSpace = str[runEnd-1].direction() == QChar::DirWS;
2298                                     start = -1;
2299                                 }
2300                                 if (end != -1 && runEnd >= end)
2301                                     break;
2302                             }
2303                         }
2304                     }
2305                 }
2306                 
2307                 text.replace(QChar('\\'), renderer->backslashAsCurrencySymbol());
2308     
2309                 if (text.length() > 0 || needSpace) {
2310                     NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init];
2311                     [attrs setObject:font forKey:NSFontAttributeName];
2312                     if (style && style->color().isValid() && style->color().alpha() != 0)
2313                         [attrs setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2314                     if (style && style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0)
2315                         [attrs setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2316
2317                     if (text.length() > 0) {
2318                         hasParagraphBreak = false;
2319                         NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString() attributes:attrs];
2320                         [result appendAttributedString: partialString];                
2321                         [partialString release];
2322                     }
2323
2324                     if (needSpace) {
2325                         [pendingStyledSpace release];
2326                         pendingStyledSpace = [[NSAttributedString alloc] initWithString:@" " attributes:attrs];
2327                     }
2328
2329                     [attrs release];
2330                 }
2331             } else {
2332                 // This is our simple HTML -> ASCII transformation:
2333                 DeprecatedString text;
2334                 if (n->hasTagName(aTag)) {
2335                     // Note the start of the <a> element.  We will add the NSLinkAttributeName
2336                     // attribute to the attributed string when navigating to the next sibling 
2337                     // of this node.
2338                     linkStartLocation = [result length];
2339                     linkStartNode = static_cast<Element*>(n);
2340                 } else if (n->hasTagName(brTag)) {
2341                     text += "\n";
2342                     hasNewLine = true;
2343                 } else if (n->hasTagName(liTag)) {
2344                     DeprecatedString listText;
2345                     Element *itemParent = listParent(static_cast<Element *>(n));
2346                     
2347                     if (!hasNewLine)
2348                         listText += '\n';
2349                     hasNewLine = true;
2350
2351                     listItems.append(static_cast<Element*>(n));
2352                     info.start = [result length];
2353                     info.end = 0;
2354                     listItemLocations.append (info);
2355                     
2356                     listText += '\t';
2357                     if (itemParent && renderer->isListItem()) {
2358                         RenderListItem *listRenderer = static_cast<RenderListItem*>(renderer);
2359
2360                         maxMarkerWidth = MAX([font pointSize], maxMarkerWidth);
2361                         switch(style->listStyleType()) {
2362                             case WebCore::DISC:
2363                                 listText += ((QChar)BULLET_CHAR);
2364                                 break;
2365                             case WebCore::CIRCLE:
2366                                 listText += ((QChar)CIRCLE_CHAR);
2367                                 break;
2368                             case WebCore::SQUARE:
2369                                 listText += ((QChar)SQUARE_CHAR);
2370                                 break;
2371                             case WebCore::LNONE:
2372                                 break;
2373                             default:
2374                                 DeprecatedString marker = listRenderer->markerStringValue();
2375                                 listText += marker;
2376                                 // Use AppKit metrics.  Will be rendered by AppKit.
2377                                 float markerWidth = [marker.getNSString() sizeWithAttributes:[NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]].width;
2378                                 maxMarkerWidth = MAX(markerWidth, maxMarkerWidth);
2379                         }
2380
2381                         listText += ' ';
2382                         listText += '\t';
2383
2384                         NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init];
2385                         [attrs setObject:font forKey:NSFontAttributeName];
2386                         if (style && style->color().isValid())
2387                             [attrs setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2388                         if (style && style->backgroundColor().isValid())
2389                             [attrs setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2390
2391                         NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:listText.getNSString() attributes:attrs];
2392                         [attrs release];
2393                         [result appendAttributedString: partialString];                
2394                         [partialString release];
2395                     }
2396                 } else if (n->hasTagName(olTag) || n->hasTagName(ulTag)) {
2397                     if (!hasNewLine)
2398                         text += "\n";
2399                     hasNewLine = true;
2400                 } else if (n->hasTagName(blockquoteTag)
2401                         || n->hasTagName(ddTag)
2402                         || n->hasTagName(divTag)
2403                         || n->hasTagName(dlTag)
2404                         || n->hasTagName(dtTag)
2405                         || n->hasTagName(hrTag)
2406                         || n->hasTagName(listingTag)
2407                         || n->hasTagName(preTag)
2408                         || n->hasTagName(tdTag)
2409                         || n->hasTagName(thTag)) {
2410                     if (!hasNewLine)
2411                         text += '\n';
2412                     hasNewLine = true;
2413                 } else if (n->hasTagName(h1Tag)
2414                         || n->hasTagName(h2Tag)
2415                         || n->hasTagName(h3Tag)
2416                         || n->hasTagName(h4Tag)
2417                         || n->hasTagName(h5Tag)
2418                         || n->hasTagName(h6Tag)
2419                         || n->hasTagName(pTag)
2420                         || n->hasTagName(trTag)) {
2421                     if (!hasNewLine)
2422                         text += '\n';
2423                     
2424                     // In certain cases, emit a paragraph break.
2425                     int bottomMargin = renderer->collapsedMarginBottom();
2426                     int fontSize = style->fontDescription().computedPixelSize();
2427                     if (bottomMargin * 2 >= fontSize) {
2428                         if (!hasParagraphBreak) {
2429                             text += '\n';
2430                             hasParagraphBreak = true;
2431                         }
2432                     }
2433                     
2434                     hasNewLine = true;
2435                 }
2436                 else if (n->hasTagName(imgTag)) {
2437                     if (pendingStyledSpace != nil) {
2438                         if (linkStartLocation == [result length])
2439                             ++linkStartLocation;
2440                         [result appendAttributedString:pendingStyledSpace];
2441                         [pendingStyledSpace release];
2442                         pendingStyledSpace = nil;
2443                     }
2444                     NSFileWrapper *fileWrapper = fileWrapperForElement(static_cast<Element *>(n));
2445                     NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWrapper:fileWrapper];
2446                     NSAttributedString *iString = [NSAttributedString attributedStringWithAttachment:attachment];
2447                     [result appendAttributedString: iString];
2448                     [attachment release];
2449                 }
2450
2451                 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()];
2452                 [result appendAttributedString: partialString];
2453                 [partialString release];
2454             }
2455         }
2456
2457         if (n == endNode)
2458             break;
2459
2460         Node *next = n->firstChild();
2461         if (!next)
2462             next = n->nextSibling();
2463
2464         while (!next && n->parentNode()) {
2465             DeprecatedString text;
2466             n = n->parentNode();
2467             if (n == endNode)
2468                 break;
2469             next = n->nextSibling();
2470
2471             if (n->hasTagName(aTag)) {
2472                 // End of a <a> element.  Create an attributed string NSLinkAttributeName attribute
2473                 // for the range of the link.  Note that we create the attributed string from the DOM, which
2474                 // will have corrected any illegally nested <a> elements.
2475                 if (linkStartNode && n == linkStartNode) {
2476                     String href = parseURL(linkStartNode->getAttribute(hrefAttr));
2477                     KURL kURL = Mac(linkStartNode->getDocument()->frame())->completeURL(href.deprecatedString());
2478                     
2479                     NSURL *URL = kURL.getNSURL();
2480                     NSRange tempRange = { linkStartLocation, [result length]-linkStartLocation }; // workaround for 4213314
2481                     [result addAttribute:NSLinkAttributeName value:URL range:tempRange];
2482                     linkStartNode = 0;
2483                 }
2484             }
2485             else if (n->hasTagName(olTag) || n->hasTagName(ulTag)) {
2486                 if (!hasNewLine)
2487                     text += '\n';
2488                 hasNewLine = true;
2489             } else if (n->hasTagName(liTag)) {
2490                 
2491                 int i, count = listItems.count();
2492                 for (i = 0; i < count; i++){
2493                     if (listItems.at(i) == n){
2494                         listItemLocations[i].end = [result length];
2495                         break;
2496                     }
2497                 }
2498                 if (!hasNewLine)
2499                     text += '\n';
2500                 hasNewLine = true;
2501             } else if (n->hasTagName(blockquoteTag) ||
2502                        n->hasTagName(ddTag) ||
2503                        n->hasTagName(divTag) ||
2504                        n->hasTagName(dlTag) ||
2505                        n->hasTagName(dtTag) ||
2506                        n->hasTagName(hrTag) ||
2507                        n->hasTagName(listingTag) ||
2508                        n->hasTagName(preTag) ||
2509                        n->hasTagName(tdTag) ||
2510                        n->hasTagName(thTag)) {
2511                 if (!hasNewLine)
2512                     text += '\n';
2513                 hasNewLine = true;
2514             } else if (n->hasTagName(pTag) ||
2515                        n->hasTagName(trTag) ||
2516                        n->hasTagName(h1Tag) ||
2517                        n->hasTagName(h2Tag) ||
2518                        n->hasTagName(h3Tag) ||
2519                        n->hasTagName(h4Tag) ||
2520                        n->hasTagName(h5Tag) ||
2521                        n->hasTagName(h6Tag)) {
2522                 if (!hasNewLine)
2523                     text += '\n';
2524                 // An extra newline is needed at the start, not the end, of these types of tags,
2525                 // so don't add another here.
2526                 hasNewLine = true;
2527             }
2528             
2529             NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()];
2530             [result appendAttributedString:partialString];
2531             [partialString release];
2532         }
2533
2534         n = next;
2535     }
2536     
2537     [pendingStyledSpace release];
2538     
2539     // Apply paragraph styles from outside in.  This ensures that nested lists correctly
2540     // override their parent's paragraph style.
2541     {
2542         unsigned i, count = listItems.count();
2543         Element *e;
2544
2545 #ifdef POSITION_LIST
2546         Node *containingBlock;
2547         int containingBlockX, containingBlockY;
2548         
2549         // Determine the position of the outermost containing block.  All paragraph
2550         // styles and tabs should be relative to this position.  So, the horizontal position of 
2551         // each item in the list (in the resulting attributed string) will be relative to position 
2552         // of the outermost containing block.
2553         if (count > 0){
2554             containingBlock = _startNode;
2555             while (containingBlock->renderer()->isInline()){
2556                 containingBlock = containingBlock->parentNode();
2557             }
2558             containingBlock->renderer()->absolutePosition(containingBlockX, containingBlockY);
2559         }
2560 #endif
2561         
2562         for (i = 0; i < count; i++){
2563             e = listItems.at(i);
2564             info = listItemLocations[i];
2565             
2566             if (info.end < info.start)
2567                 info.end = [result length];
2568                 
2569             RenderObject *r = e->renderer();
2570             RenderStyle *style = r->style();
2571
2572             int rx;
2573             NSFont *font = style->font().getNSFont();
2574             float pointSize = [font pointSize];
2575
2576 #ifdef POSITION_LIST
2577             int ry;
2578             r->absolutePosition(rx, ry);
2579             rx -= containingBlockX;
2580             
2581             // Ensure that the text is indented at least enough to allow for the markers.
2582             rx = MAX(rx, (int)maxMarkerWidth);
2583 #else
2584             rx = (int)MAX(maxMarkerWidth, pointSize);
2585 #endif
2586
2587             // The bullet text will be right aligned at the first tab marker, followed
2588             // by a space, followed by the list item text.  The space is arbitrarily
2589             // picked as pointSize*2/3.  The space on the first line of the text item
2590             // is established by a left aligned tab, on subsequent lines it's established
2591             // by the head indent.
2592             NSMutableParagraphStyle *mps = [[NSMutableParagraphStyle alloc] init];
2593             [mps setFirstLineHeadIndent: 0];
2594             [mps setHeadIndent: rx];
2595             [mps setTabStops:[NSArray arrayWithObjects:
2596                         [[[NSTextTab alloc] initWithType:NSRightTabStopType location:rx-(pointSize*2/3)] autorelease],
2597                         [[[NSTextTab alloc] initWithType:NSLeftTabStopType location:rx] autorelease],
2598                         nil]];
2599             NSRange tempRange = { info.start, info.end-info.start }; // workaround for 4213314
2600             [result addAttribute:NSParagraphStyleAttributeName value:mps range:tempRange];
2601             [mps release];
2602         }
2603     }
2604
2605     return result;
2606
2607     END_BLOCK_OBJC_EXCEPTIONS;
2608
2609     return nil;
2610 }
2611
2612 NSImage *FrameMac::imageFromRect(NSRect rect) const
2613 {
2614     NSView *view = d->m_view->getDocumentView();
2615     if (!view)
2616         return nil;
2617     
2618     NSImage *resultImage;
2619     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2620     
2621     NSRect bounds = [view bounds];
2622     resultImage = [[[NSImage alloc] initWithSize:rect.size] autorelease];
2623
2624     if (rect.size.width != 0 && rect.size.height != 0) {
2625         [resultImage setFlipped:YES];
2626         [resultImage lockFocus];
2627
2628         CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
2629
2630         CGContextSaveGState(context);
2631         CGContextTranslateCTM(context, bounds.origin.x - rect.origin.x, bounds.origin.y - rect.origin.y);
2632         [view drawRect:rect];
2633         CGContextRestoreGState(context);
2634
2635         [resultImage unlockFocus];
2636         [resultImage setFlipped:NO];
2637     }
2638
2639     return resultImage;
2640
2641     END_BLOCK_OBJC_EXCEPTIONS;
2642     
2643     return nil;
2644 }
2645
2646 NSImage *FrameMac::selectionImage() const
2647 {
2648     d->m_drawSelectionOnly = true;  // invoke special drawing mode
2649     NSImage *result = imageFromRect(visibleSelectionRect());
2650     d->m_drawSelectionOnly = false;
2651     return result;
2652 }
2653
2654 NSImage *FrameMac::snapshotDragImage(Node *node, NSRect *imageRect, NSRect *elementRect) const
2655 {
2656     RenderObject *renderer = node->renderer();
2657     if (!renderer)
2658         return nil;
2659     
2660     renderer->updateDragState(true);    // mark dragged nodes (so they pick up the right CSS)
2661     d->m_doc->updateLayout();        // forces style recalc - needed since changing the drag state might
2662                                         // imply new styles, plus JS could have changed other things
2663     IntRect topLevelRect;
2664     NSRect paintingRect = renderer->paintingRootRect(topLevelRect);
2665
2666     d->m_elementToDraw = node;              // invoke special sub-tree drawing mode
2667     NSImage *result = imageFromRect(paintingRect);
2668     renderer->updateDragState(false);
2669     d->m_doc->updateLayout();
2670     d->m_elementToDraw = 0;
2671
2672     if (elementRect)
2673         *elementRect = topLevelRect;
2674     if (imageRect)
2675         *imageRect = paintingRect;
2676     return result;
2677 }
2678
2679 NSFont *FrameMac::fontForSelection(bool *hasMultipleFonts) const
2680 {
2681     if (hasMultipleFonts)
2682         *hasMultipleFonts = false;
2683
2684     if (!d->m_selection.isRange()) {
2685         Node *nodeToRemove;
2686         RenderStyle *style = styleForSelectionStart(nodeToRemove); // sets nodeToRemove
2687
2688         NSFont *result = 0;
2689         if (style)
2690             result = style->font().getNSFont();
2691         
2692         if (nodeToRemove) {
2693             ExceptionCode ec;
2694             nodeToRemove->remove(ec);
2695             ASSERT(ec == 0);
2696         }
2697
2698         return result;
2699     }
2700
2701     NSFont *font = nil;
2702
2703     RefPtr<Range> range = d->m_selection.toRange();
2704     Node *startNode = range->editingStartPosition().node();
2705     if (startNode != nil) {
2706         Node *pastEnd = range->pastEndNode();
2707         // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one
2708         // unreproducible case where this didn't happen, so check for nil also.
2709         for (Node *n = startNode; n && n != pastEnd; n = n->traverseNextNode()) {
2710             RenderObject *renderer = n->renderer();
2711             if (!renderer)
2712                 continue;
2713             // FIXME: Are there any node types that have renderers, but that we should be skipping?
2714             NSFont *f = renderer->style()->font().getNSFont();
2715             if (!font) {
2716                 font = f;
2717                 if (!hasMultipleFonts)
2718                     break;
2719             } else if (font != f) {
2720                 *hasMultipleFonts = true;
2721                 break;
2722             }
2723         }
2724     }
2725
2726     return font;
2727 }
2728
2729 NSDictionary *FrameMac::fontAttributesForSelectionStart() const
2730 {
2731     Node *nodeToRemove;
2732     RenderStyle *style = styleForSelectionStart(nodeToRemove);
2733     if (!style)
2734         return nil;
2735
2736     NSMutableDictionary *result = [NSMutableDictionary dictionary];
2737
2738     if (style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0)
2739         [result setObject:nsColor(style->backgroundColor()) forKey:NSBackgroundColorAttributeName];
2740
2741     if (style->font().getNSFont())
2742         [result setObject:style->font().getNSFont() forKey:NSFontAttributeName];
2743
2744     if (style->color().isValid() && style->color() != Color::black)
2745         [result setObject:nsColor(style->color()) forKey:NSForegroundColorAttributeName];
2746
2747     ShadowData *shadow = style->textShadow();
2748     if (shadow) {
2749         NSShadow *s = [[NSShadow alloc] init];
2750         [s setShadowOffset:NSMakeSize(shadow->x, shadow->y)];
2751         [s setShadowBlurRadius:shadow->blur];
2752         [s setShadowColor:nsColor(shadow->color)];
2753         [result setObject:s forKey:NSShadowAttributeName];
2754     }
2755
2756     int decoration = style->textDecorationsInEffect();
2757     if (decoration & WebCore::LINE_THROUGH)
2758         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSStrikethroughStyleAttributeName];
2759
2760     int superscriptInt = 0;
2761     switch (style->verticalAlign()) {
2762         case WebCore::BASELINE:
2763         case WebCore::BOTTOM:
2764         case WebCore::BASELINE_MIDDLE:
2765         case WebCore::LENGTH:
2766         case WebCore::MIDDLE:
2767         case WebCore::TEXT_BOTTOM:
2768         case WebCore::TEXT_TOP:
2769         case WebCore::TOP:
2770             break;
2771         case WebCore::SUB:
2772             superscriptInt = -1;
2773             break;
2774         case WebCore::SUPER:
2775             superscriptInt = 1;
2776             break;
2777     }
2778     if (superscriptInt)
2779         [result setObject:[NSNumber numberWithInt:superscriptInt] forKey:NSSuperscriptAttributeName];
2780
2781     if (decoration & WebCore::UNDERLINE)
2782         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
2783
2784     if (nodeToRemove) {
2785         ExceptionCode ec = 0;
2786         nodeToRemove->remove(ec);
2787         ASSERT(ec == 0);
2788     }
2789
2790     return result;
2791 }
2792
2793 NSWritingDirection FrameMac::baseWritingDirectionForSelectionStart() const
2794 {
2795     NSWritingDirection result = NSWritingDirectionLeftToRight;
2796
2797     Position pos = VisiblePosition(d->m_selection.start(), d->m_selection.affinity()).deepEquivalent();
2798     Node *node = pos.node();
2799     if (!node || !node->renderer() || !node->renderer()->containingBlock())
2800         return result;
2801     RenderStyle *style = node->renderer()->containingBlock()->style();
2802     if (!style)
2803         return result;
2804         
2805     switch (style->direction()) {
2806         case WebCore::LTR:
2807             result = NSWritingDirectionLeftToRight;
2808             break;
2809         case WebCore::RTL:
2810             result = NSWritingDirectionRightToLeft;
2811             break;
2812     }
2813
2814     return result;
2815 }
2816
2817 void FrameMac::tokenizerProcessedData()
2818 {
2819     if (d->m_doc)
2820         checkCompleted();
2821     [_bridge tokenizerProcessedData];
2822 }
2823
2824 void FrameMac::setBridge(WebCoreFrameBridge *bridge)
2825
2826     if (_bridge == bridge)
2827         return;
2828
2829     KWQRetain(bridge);
2830     KWQRelease(_bridge);
2831     _bridge = bridge;
2832 }
2833
2834 String FrameMac::overrideMediaType() const
2835 {
2836     NSString *overrideType = [_bridge overrideMediaType];
2837     if (overrideType)
2838         return overrideType;
2839     return String();
2840 }
2841
2842 void FrameMac::setDisplaysWithFocusAttributes(bool flag)
2843 {
2844     if (d->m_isFocused == flag)
2845         return;
2846     
2847     Frame::setDisplaysWithFocusAttributes(flag);
2848     Document *doc = document();
2849     // Mac Specific: Changing the tint of controls from clear to aqua/graphite and vice versa.  We
2850     // do a "fake" paint.  When the theme gets a paint call, it can then do an invalidate.
2851     if (doc && d->m_view && d->m_view->getDocumentView() && theme()->supportsControlTints() && renderer()) {
2852         doc->updateLayout(); // Ensure layout is up to date.
2853         IntRect visibleRect(enclosingIntRect(d->m_view->visibleContentRect()));
2854         GraphicsContext p;
2855         p.setUpdatingControlTints(true);
2856         paint(&p, visibleRect);
2857     }
2858 }
2859
2860 NSColor *FrameMac::bodyBackgroundColor() const
2861 {
2862     if (document() && document()->body() && document()->body()->renderer()) {
2863         Color bgColor = document()->body()->renderer()->style()->backgroundColor();
2864         if (bgColor.isValid())
2865             return nsColor(bgColor);
2866     }
2867     return nil;
2868 }
2869
2870 WebCoreKeyboardUIMode FrameMac::keyboardUIMode() const
2871 {
2872     BEGIN_BLOCK_OBJC_EXCEPTIONS;
2873     return [_bridge keyboardUIMode];
2874     END_BLOCK_OBJC_EXCEPTIONS;
2875
2876     return WebCoreKeyboardAccessDefault;
2877 }
2878
2879 void FrameMac::didTellBridgeAboutLoad(const WebCore::String& URL)
2880 {
2881     urlsBridgeKnowsAbout.add(URL.impl());
2882 }
2883
2884 bool FrameMac::haveToldBridgeAboutLoad(const WebCore::String& URL)
2885 {
2886     return urlsBridgeKnowsAbout.contains(URL.impl());
2887 }
2888
2889 void FrameMac::clear()
2890 {
2891     urlsBridgeKnowsAbout.clear();
2892     setMarkedTextRange(0, nil, nil);
2893     Frame::clear();
2894 }
2895
2896 void FrameMac::print()
2897 {
2898     [Mac(this)->_bridge print];
2899 }
2900
2901 KJS::Bindings::Instance *FrameMac::getAppletInstanceForWidget(Widget *widget)
2902 {
2903     NSView *aView = widget->getView();
2904     if (!aView)
2905         return 0;
2906     jobject applet;
2907     
2908     // Get a pointer to the actual Java applet instance.
2909     if ([_bridge respondsToSelector:@selector(getAppletInView:)])
2910         applet = [_bridge getAppletInView:aView];
2911     else
2912         applet = [_bridge pollForAppletInView:aView];
2913     
2914     if (applet) {
2915         // Wrap the Java instance in a language neutral binding and hand
2916         // off ownership to the APPLET element.
2917         KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2918         KJS::Bindings::Instance *instance = KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::JavaLanguage, applet, executionContext);        
2919         return instance;
2920     }
2921     
2922     return 0;
2923 }
2924
2925 static KJS::Bindings::Instance *getInstanceForView(NSView *aView)
2926 {
2927     if ([aView respondsToSelector:@selector(objectForWebScript)]){
2928         id object = [aView objectForWebScript];
2929         if (object) {
2930             KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2931             return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::ObjectiveCLanguage, object, executionContext);
2932         }
2933     }
2934     else if ([aView respondsToSelector:@selector(pluginScriptableObject)]){
2935         void *object = [aView pluginScriptableObject];
2936         if (object) {
2937             KJS::Bindings::RootObject *executionContext = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
2938             return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::CLanguage, object, executionContext);
2939         }
2940     }
2941     return 0;
2942 }
2943
2944 KJS::Bindings::Instance *FrameMac::getEmbedInstanceForWidget(Widget *widget)
2945 {
2946     return getInstanceForView(widget->getView());
2947 }
2948
2949 KJS::Bindings::Instance *FrameMac::getObjectInstanceForWidget(Widget *widget)
2950 {
2951     return getInstanceForView(widget->getView());
2952 }
2953
2954 void FrameMac::addPluginRootObject(KJS::Bindings::RootObject *root)
2955 {
2956     m_rootObjects.append(root);
2957 }
2958
2959 void FrameMac::cleanupPluginRootObjects()
2960 {
2961     JSLock lock;
2962
2963     unsigned count = m_rootObjects.size();
2964     for (unsigned i = 0; i < count; i++)
2965         m_rootObjects[i]->removeAllNativeReferences();
2966     m_rootObjects.clear();
2967 }
2968
2969 void FrameMac::registerCommandForUndoOrRedo(const EditCommandPtr &cmd, bool isRedo)
2970 {
2971     ASSERT(cmd.get());
2972     KWQEditCommand *kwq = [KWQEditCommand commandWithEditCommand:cmd.get()];
2973     NSUndoManager *undoManager = [_bridge undoManager];
2974     [undoManager registerUndoWithTarget:_bridge selector:(isRedo ? @selector(redoEditing:) : @selector(undoEditing:)) object:kwq];
2975     NSString *actionName = [_bridge nameForUndoAction:static_cast<WebUndoAction>(cmd.editingAction())];
2976     if (actionName != nil)
2977         [undoManager setActionName:actionName];
2978     _haveUndoRedoOperations = YES;
2979 }
2980
2981 void FrameMac::registerCommandForUndo(const EditCommandPtr &cmd)
2982 {
2983     registerCommandForUndoOrRedo(cmd, NO);
2984 }
2985
2986 void FrameMac::registerCommandForRedo(const EditCommandPtr &cmd)
2987 {
2988     registerCommandForUndoOrRedo(cmd, YES);
2989 }
2990
2991 void FrameMac::clearUndoRedoOperations()
2992 {
2993     if (_haveUndoRedoOperations) {
2994         [[_bridge undoManager] removeAllActionsWithTarget:_bridge];
2995         _haveUndoRedoOperations = NO;
2996     }
2997 }
2998
2999 void FrameMac::issueUndoCommand()
3000 {
3001     if (canUndo())
3002         [[_bridge undoManager] undo];
3003 }
3004
3005 void FrameMac::issueRedoCommand()
3006 {
3007     if (canRedo())
3008         [[_bridge undoManager] redo];
3009 }
3010
3011 void FrameMac::issueCutCommand()
3012 {
3013     [_bridge issueCutCommand];
3014 }
3015
3016 void FrameMac::issueCopyCommand()
3017 {
3018     [_bridge issueCopyCommand];
3019 }
3020
3021 void FrameMac::issuePasteCommand()
3022 {
3023     [_bridge issuePasteCommand];
3024 }
3025
3026 void FrameMac::issuePasteAndMatchStyleCommand()
3027 {
3028     [_bridge issuePasteAndMatchStyleCommand];
3029 }
3030
3031 void FrameMac::issueTransposeCommand()
3032 {
3033     [_bridge issueTransposeCommand];
3034 }
3035
3036 bool FrameMac::canUndo() const
3037 {
3038     return [[Mac(this)->_bridge undoManager] canUndo];
3039 }
3040
3041 bool FrameMac::canRedo() const
3042 {
3043     return [[Mac(this)->_bridge undoManager] canRedo];
3044 }
3045
3046 bool FrameMac::canPaste() const
3047 {
3048     return [Mac(this)->_bridge canPaste];
3049 }
3050
3051 void FrameMac::markMisspellingsInAdjacentWords(const VisiblePosition &p)
3052 {
3053     if (![_bridge isContinuousSpellCheckingEnabled])
3054         return;
3055     markMisspellings(SelectionController(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)));
3056 }
3057
3058 void FrameMac::markMisspellings(const SelectionController &selection)
3059 {
3060     // This function is called with a selection already expanded to word boundaries.
3061     // Might be nice to assert that here.
3062
3063     if (![_bridge isContinuousSpellCheckingEnabled])
3064         return;
3065
3066     RefPtr<Range> searchRange(selection.toRange());
3067     if (!searchRange || searchRange->isDetached())
3068         return;
3069     
3070     // If we're not in an editable node, bail.
3071     int exception = 0;
3072     Node *editableNode = searchRange->startContainer(exception);
3073     if (!editableNode->isContentEditable())
3074         return;
3075     
3076     // Get the spell checker if it is available
3077     NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
3078     if (checker == nil)
3079         return;
3080     
3081     WordAwareIterator it(searchRange.get());
3082     
3083     while (!it.atEnd()) {      // we may be starting at the end of the doc, and already by atEnd
3084         const QChar *chars = it.characters();
3085         int len = it.length();
3086         if (len > 1 || !chars[0].isSpace()) {
3087             NSString *chunk = [[NSString alloc] initWithCharactersNoCopy:(unichar *)chars length:len freeWhenDone:NO];
3088             int startIndex = 0;
3089             // Loop over the chunk to find each misspelling in it.
3090             while (startIndex < len) {
3091                 NSRange misspelling = [checker checkSpellingOfString:chunk startingAt:startIndex language:nil wrap:NO inSpellDocumentWithTag:[_bridge spellCheckerDocumentTag] wordCount:NULL];
3092                 if (misspelling.length == 0)
3093                     break;
3094                 else {
3095                     // Build up result range and string.  Note the misspelling may span many text nodes,
3096                     // but the CharIterator insulates us from this complexity
3097                     RefPtr<Range> misspellingRange(rangeOfContents(document()));
3098                     CharacterIterator chars(it.range().get());
3099                     chars.advance(misspelling.location);
3100                     misspellingRange->setStart(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
3101                     chars.advance(misspelling.length);
3102                     misspellingRange->setEnd(chars.range()->startContainer(exception), chars.range()->startOffset(exception), exception);
3103                     // Mark misspelling in document.
3104                     document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
3105                     startIndex = misspelling.location + misspelling.length;
3106                 }
3107             }
3108             [chunk release];
3109         }
3110     
3111         it.advance();
3112     }
3113 }
3114
3115 void FrameMac::respondToChangedSelection(const SelectionController &oldSelection, bool closeTyping)
3116 {
3117     if (document()) {
3118         if ([_bridge isContinuousSpellCheckingEnabled]) {
3119             SelectionController oldAdjacentWords = SelectionController();
3120             
3121             // If this is a change in selection resulting from a delete operation, oldSelection may no longer
3122             // be in the document.
3123             if (oldSelection.start().node() && oldSelection.start().node()->inDocument()) {
3124                 VisiblePosition oldStart(oldSelection.start(), oldSelection.affinity());
3125                 oldAdjacentWords = SelectionController(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));   
3126             }
3127
3128             VisiblePosition newStart(selection().start(), selection().affinity());
3129             SelectionController newAdjacentWords(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
3130
3131             if (oldAdjacentWords != newAdjacentWords) {
3132                 // Mark misspellings in the portion that was previously unmarked because of
3133                 // the proximity of the start of the selection. We only spell check words in
3134                 // the vicinity of the start of the old selection because the spelling checker
3135                 // is not fast enough to do a lot of spelling checking implicitly. This matches
3136                 // AppKit. This function is really the only code that knows that rule. The
3137                 // markMisspellings function is prepared to handler larger ranges.
3138
3139                 // When typing we check spelling elsewhere, so don't redo it here.
3140                 if (closeTyping)
3141                     markMisspellings(oldAdjacentWords);
3142
3143                 // This only erases a marker in the first word of the selection.
3144                 // Perhaps peculiar, but it matches AppKit.
3145                 document()->removeMarkers(newAdjacentWords.toRange().get(), DocumentMarker::Spelling);
3146             }
3147         } else
3148             // When continuous spell checking is off, no markers appear after the selection changes.
3149             document()->removeMarkers(DocumentMarker::Spelling);
3150     }
3151
3152     [_bridge respondToChangedSelection];
3153 }
3154
3155 bool FrameMac::shouldChangeSelection(const SelectionController &oldSelection, const SelectionController &newSelection, WebCore::EAffinity affinity, bool stillSelecting) const
3156 {
3157     return [_bridge shouldChangeSelectedDOMRange:[DOMRange _rangeWith:oldSelection.toRange().get()]
3158                                       toDOMRange:[DOMRange _rangeWith:newSelection.toRange().get()]
3159                                         affinity:static_cast<NSSelectionAffinity>(affinity)
3160                                   stillSelecting:stillSelecting];
3161 }
3162
3163 void FrameMac::respondToChangedContents()
3164 {
3165     if (AccessibilityObjectCache::accessibilityEnabled())
3166         renderer()->document()->getAccObjectCache()->postNotificationToTopWebArea(renderer(), "AXValueChanged");
3167     [_bridge respondToChangedContents];
3168 }
3169
3170 bool FrameMac::isContentEditable() const
3171 {
3172     return Frame::isContentEditable() || [_bridge isEditable];
3173 }
3174
3175 bool FrameMac::shouldBeginEditing(const Range *range) const
3176 {
3177     ASSERT(range);
3178     return [_bridge shouldBeginEditing:[DOMRange _rangeWith:const_cast<Range *>(range)]];
3179 }
3180
3181 bool FrameMac::shouldEndEditing(const Range *range) const
3182 {
3183     ASSERT(range);
3184     return [_bridge shouldEndEditing:[DOMRange _rangeWith:const_cast<Range *>(range)]];
3185 }
3186
3187 void FrameMac::didBeginEditing() const
3188 {
3189     [_bridge didBeginEditing];
3190 }
3191
3192 void FrameMac::didEndEditing() const
3193 {
3194     [_bridge didEndEditing];
3195 }
3196
3197 static DeprecatedValueList<MarkedTextUnderline> convertAttributesToUnderlines(const Range *markedTextRange, NSArray *attributes, NSArray *ranges)
3198 {
3199     DeprecatedValueList<MarkedTextUnderline> result;
3200
3201     int exception = 0;
3202     int baseOffset = markedTextRange->startOffset(exception);
3203
3204     unsigned length = [attributes count];
3205     ASSERT([ranges count] == length);
3206
3207     for (unsigned i = 0; i < length; i++) {
3208         NSNumber *style = [[attributes objectAtIndex:i] objectForKey:NSUnderlineStyleAttributeName];
3209         if (!style)
3210             continue;
3211         NSRange range = [[ranges objectAtIndex:i] rangeValue];
3212         NSColor *color = [[attributes objectAtIndex:i] objectForKey:NSUnderlineColorAttributeName];
3213         Color qColor = Color::black;
3214         if (color) {
3215             NSColor* deviceColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
3216             qColor = Color(makeRGBA((int)(255 * [deviceColor redComponent]),
3217                                     (int)(255 * [deviceColor blueComponent]),
3218                                     (int)(255 * [deviceColor greenComponent]),
3219                                     (int)(255 * [deviceColor alphaComponent])));
3220         }
3221
3222         result.append(MarkedTextUnderline(range.location + baseOffset, 
3223                                           range.location + baseOffset + range.length, 
3224                                           qColor,
3225                                           [style intValue] > 1));
3226     }
3227
3228     return result;
3229 }
3230
3231 void FrameMac::setMarkedTextRange(const Range *range, NSArray *attributes, NSArray *ranges)
3232 {
3233     int exception = 0;
3234
3235     ASSERT(!range || range->startContainer(exception) == range->endContainer(exception));
3236     ASSERT(!range || range->collapsed(exception) || range->startContainer(exception)->isTextNode());
3237
3238     if (attributes == nil) {
3239         d->m_markedTextUsesUnderlines = false;
3240         d->m_markedTextUnderlines.clear();
3241     } else {
3242         d->m_markedTextUsesUnderlines = true;
3243         d->m_markedTextUnderlines = convertAttributesToUnderlines(range, attributes, ranges);
3244     }
3245
3246     if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
3247         m_markedTextRange->startContainer(exception)->renderer()->repaint();
3248
3249     if (range && range->collapsed(exception))
3250         m_markedTextRange = 0;
3251     else
3252         m_markedTextRange = const_cast<Range *>(range);
3253
3254     if (m_markedTextRange.get() && document() && m_markedTextRange->startContainer(exception)->renderer())
3255         m_markedTextRange->startContainer(exception)->renderer()->repaint();
3256 }
3257
3258 bool FrameMac::canGoBackOrForward(int distance) const
3259 {
3260     return [_bridge canGoBackOrForward:distance];
3261 }
3262
3263 void FrameMac::didFirstLayout()
3264 {
3265     [_bridge didFirstLayout];
3266 }
3267
3268 NSMutableDictionary *FrameMac::dashboardRegionsDictionary()
3269 {
3270     Document *doc = document();
3271     if (!doc)
3272         return nil;
3273
3274     const DeprecatedValueList<DashboardRegionValue> regions = doc->dashboardRegions();
3275     unsigned i, count = regions.count();
3276
3277     // Convert the DeprecatedValueList<DashboardRegionValue> into a NSDictionary of WebDashboardRegions
3278     NSMutableDictionary *webRegions = [[[NSMutableDictionary alloc] initWithCapacity:count] autorelease];
3279     for (i = 0; i < count; i++) {
3280         DashboardRegionValue region = regions[i];
3281
3282         if (region.type == StyleDashboardRegion::None)
3283             continue;
3284         
3285         NSString *label = region.label;
3286         WebDashboardRegionType type = WebDashboardRegionTypeNone;
3287         if (region.type == StyleDashboardRegion::Circle)
3288             type = WebDashboardRegionTypeCircle;
3289         else if (region.type == StyleDashboardRegion::Rectangle)
3290             type = WebDashboardRegionTypeRectangle;
3291         NSMutableArray *regionValues = [webRegions objectForKey:label];
3292         if (!regionValues) {
3293             regionValues = [NSMutableArray array];
3294             [webRegions setObject:regionValues forKey:label];
3295         }
3296         
3297         WebDashboardRegion *webRegion = [[[WebDashboardRegion alloc] initWithRect:region.bounds clip:region.clip type:type] autorelease];
3298         [regionValues addObject:webRegion];
3299     }
3300     
3301     return webRegions;
3302 }
3303
3304 void FrameMac::dashboardRegionsChanged()
3305 {
3306     NSMutableDictionary *webRegions = dashboardRegionsDictionary();
3307     [_bridge dashboardRegionsChanged:webRegions];
3308 }
3309
3310 bool FrameMac::isCharacterSmartReplaceExempt(const QChar &c, bool isPreviousChar)
3311 {
3312     return [_bridge isCharacterSmartReplaceExempt:c.unicode() isPreviousCharacter:isPreviousChar];
3313 }
3314
3315 void FrameMac::handledOnloadEvents()
3316 {
3317     [_bridge handledOnloadEvents];
3318 }
3319
3320 bool FrameMac::shouldClose()
3321 {
3322     BEGIN_BLOCK_OBJC_EXCEPTIONS;
3323
3324     if (![_bridge canRunBeforeUnloadConfirmPanel])
3325         return true;
3326
3327     RefPtr<Document> doc = document();
3328     if (!doc)
3329         return true;
3330     HTMLElement* body = doc->body();
3331     if (!body)
3332         return true;
3333
3334     RefPtr<BeforeUnloadEvent> event = new BeforeUnloadEvent;
3335     event->setTarget(doc.get());
3336     doc->handleWindowEvent(event.get(), false);
3337
3338     if (!event->defaultPrevented() && doc)
3339         doc->defaultEventHandler(event.get());
3340     if (event->result().isNull())
3341         return true;
3342
3343     String text = event->result();
3344     text.replace(QChar('\\'), backslashAsCurrencySymbol());
3345
3346     return [_bridge runBeforeUnloadConfirmPanelWithMessage:text];
3347
3348     END_BLOCK_OBJC_EXCEPTIONS;
3349
3350     return true;
3351 }
3352
3353 void FrameMac::dragSourceMovedTo(const PlatformMouseEvent& event)
3354 {
3355     if (_dragSrc && _dragSrcMayBeDHTML)
3356         // for now we don't care if event handler cancels default behavior, since there is none
3357         dispatchDragSrcEvent(dragEvent, event);
3358 }
3359
3360 void FrameMac::dragSourceEndedAt(const PlatformMouseEvent& event, NSDragOperation operation)
3361 {
3362     if (_dragSrc && _dragSrcMayBeDHTML) {
3363         _dragClipboard->setDestinationOperation(operation);
3364         // for now we don't care if event handler cancels default behavior, since there is none
3365         dispatchDragSrcEvent(dragendEvent, event);
3366     }
3367     freeClipboard();
3368     _dragSrc = 0;
3369 }
3370
3371 // returns if we should continue "default processing", i.e., whether eventhandler canceled
3372 bool FrameMac::dispatchDragSrcEvent(const AtomicString &eventType, const PlatformMouseEvent& event) const
3373 {
3374     bool noDefaultProc = d->m_view->dispatchDragEvent(eventType, _dragSrc.get(), event, _dragClipboard.get());
3375     return !noDefaultProc;
3376 }
3377
3378 void FrameMac::detachFromView()
3379 {
3380     setView(0);
3381 }
3382
3383 }