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