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