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