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