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