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