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