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