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