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