Reviewed by Maciej.
[WebKit-https.git] / WebCore / kwq / KWQKHTMLPart.mm
1 /*
2  * Copyright (C) 2003 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 "KWQDOMNode.h"
29 #import "KWQDummyView.h"
30 #import "KWQExceptions.h"
31 #import "KWQKJobClasses.h"
32 #import "KWQLogging.h"
33 #import "KWQPageState.h"
34 #import "KWQPrinter.h"
35 #import "KWQWindowWidget.h"
36 #import "WebCoreBridge.h"
37 #import "WebCoreDOMPrivate.h"
38 #import "WebCoreViewFactory.h"
39 #import "csshelper.h"
40 #import "html_documentimpl.h"
41 #import "htmltokenizer.h"
42 #import "khtmlpart_p.h"
43 #import "khtmlview.h"
44 #import "kjs_binding.h"
45 #import "kjs_window.h"
46 #import "misc/htmlattrs.h"
47 #import "qscrollbar.h"
48 #import "render_canvas.h"
49 #import "render_frames.h"
50 #import "render_image.h"
51 #import "render_list.h"
52 #import "render_style.h"
53 #import "render_table.h"
54 #import "render_text.h"
55 #import "xml/dom2_eventsimpl.h"
56 #import <JavaScriptCore/property_map.h>
57
58 #undef _KWQ_TIMING
59
60 using DOM::DocumentImpl;
61 using DOM::DOMString;
62 using DOM::ElementImpl;
63 using DOM::EventImpl;
64 using DOM::Node;
65
66 using khtml::Cache;
67 using khtml::ChildFrame;
68 using khtml::Decoder;
69 using khtml::MouseDoubleClickEvent;
70 using khtml::MouseMoveEvent;
71 using khtml::MousePressEvent;
72 using khtml::MouseReleaseEvent;
73 using khtml::parseURL;
74 using khtml::PRE;
75 using khtml::RenderCanvas;
76 using khtml::RenderImage;
77 using khtml::RenderLayer;
78 using khtml::RenderListItem;
79 using khtml::RenderObject;
80 using khtml::RenderPart;
81 using khtml::RenderStyle;
82 using khtml::RenderTableCell;
83 using khtml::RenderText;
84 using khtml::RenderWidget;
85 using khtml::InlineTextBoxArray;
86 using khtml::VISIBLE;
87
88 using KIO::Job;
89
90 using KJS::SavedBuiltins;
91 using KJS::SavedProperties;
92 using KJS::ScheduledAction;
93 using KJS::Window;
94
95 using KParts::ReadOnlyPart;
96 using KParts::URLArgs;
97
98 NSEvent *KWQKHTMLPart::_currentEvent = nil;
99 NSResponder *KWQKHTMLPart::_firstResponderAtMouseDownTime = nil;
100
101 void KHTMLPart::completed()
102 {
103     KWQ(this)->_completed.call();
104 }
105
106 void KHTMLPart::completed(bool arg)
107 {
108     KWQ(this)->_completed.call(arg);
109 }
110
111 void KHTMLPart::nodeActivated(const Node &)
112 {
113 }
114
115 bool KHTMLPart::openURL(const KURL &URL)
116 {
117     ASSERT_NOT_REACHED();
118     return true;
119 }
120
121 void KHTMLPart::onURL(const QString &)
122 {
123 }
124
125 void KHTMLPart::setStatusBarText(const QString &status)
126 {
127     KWQ(this)->setStatusBarText(status);
128 }
129
130 void KHTMLPart::started(Job *j)
131 {
132     KWQ(this)->_started.call(j);
133 }
134
135 static void redirectionTimerMonitor(void *context)
136 {
137     KWQKHTMLPart *kwq = static_cast<KWQKHTMLPart *>(context);
138     kwq->redirectionTimerStartedOrStopped();
139 }
140
141 KWQKHTMLPart::KWQKHTMLPart()
142     : _started(this, SIGNAL(started(KIO::Job *)))
143     , _completed(this, SIGNAL(completed()))
144     , _completedWithBool(this, SIGNAL(completed(bool)))
145     , _mouseDownView(nil)
146     , _sendingEventToSubview(false)
147     , _mouseDownMayStartDrag(false)
148     , _mouseDownMayStartSelect(false)
149     , _formValuesAboutToBeSubmitted(nil)
150     , _formAboutToBeSubmitted(nil)
151     , _windowWidget(NULL)
152     , _usesInactiveTextBackgroundColor(false)
153 {
154     // Must init the cache before connecting to any signals
155     Cache::init();
156
157     // The widget is made outside this class in our case.
158     KHTMLPart::init( 0, DefaultGUI );
159
160     mutableInstances().prepend(this);
161     d->m_redirectionTimer.setMonitor(redirectionTimerMonitor, this);
162 }
163
164 KWQKHTMLPart::~KWQKHTMLPart()
165 {
166     mutableInstances().remove(this);
167     if (d->m_view) {
168         d->m_view->deref();
169     }
170     // these are all basic Foundation classes and our own classes - we
171     // know they will not raise in dealloc, so no need to block
172     // exceptions.
173     [_formValuesAboutToBeSubmitted release];
174     [_formAboutToBeSubmitted release];
175     delete _windowWidget;
176 }
177
178 void KWQKHTMLPart::setSettings (KHTMLSettings *settings)
179 {
180     d->m_settings = settings;
181 }
182
183 QString KWQKHTMLPart::generateFrameName()
184 {
185     NSString * volatile name = @"";
186
187     KWQ_BLOCK_NS_EXCEPTIONS;
188     name = [_bridge generateFrameName];
189     KWQ_UNBLOCK_NS_EXCEPTIONS;
190
191     return QString::fromNSString(name);
192 }
193
194 void KWQKHTMLPart::provisionalLoadStarted()
195 {
196     // we don't want to wait until we get an actual http response back
197     // to cancel pending redirects, otherwise they might fire before
198     // that happens.
199     cancelRedirection(true);
200 }
201
202 bool KWQKHTMLPart::openURL(const KURL &url)
203 {
204     volatile bool onLoad = false;
205     
206     if (jScript() && jScript()->interpreter()) {
207         KHTMLPart *rootPart;
208         
209         rootPart = this;
210         while (rootPart->parentPart() != 0)
211             rootPart = parentPart();
212         KJS::ScriptInterpreter *interpreter = static_cast<KJS::ScriptInterpreter *>(KJSProxy::proxy(rootPart)->interpreter());
213         DOM::Event *evt = interpreter->getCurrentEvent();
214         
215         if (evt) {
216             onLoad = (evt->type() == "load");
217         }
218     }
219
220     KWQ_BLOCK_NS_EXCEPTIONS;
221
222     // FIXME: The lack of args here to get the reload flag from
223     // indicates a problem in how we use KHTMLPart::processObjectRequest,
224     // where we are opening the URL before the args are set up.
225     [_bridge loadURL:url.getNSURL()
226             referrer:[_bridge referrer]
227               reload:NO
228               onLoadEvent:onLoad
229               target:nil
230      triggeringEvent:nil
231                 form:nil
232           formValues:nil];
233
234     KWQ_UNBLOCK_NS_EXCEPTIONS;
235
236     return true;
237 }
238
239 void KWQKHTMLPart::openURLRequest(const KURL &url, const URLArgs &args)
240 {
241     KWQ_BLOCK_NS_EXCEPTIONS;
242
243     [_bridge loadURL:url.getNSURL()
244             referrer:[_bridge referrer]
245               reload:args.reload
246               onLoadEvent:false
247               target:args.frameName.getNSString()
248      triggeringEvent:nil
249                 form:nil
250           formValues:nil];
251
252     KWQ_UNBLOCK_NS_EXCEPTIONS;
253 }
254
255 void KWQKHTMLPart::didNotOpenURL(const KURL &URL)
256 {
257     if (_submittedFormURL == URL) {
258         _submittedFormURL = KURL();
259     }
260 }
261
262 // Scans logically forward from "start", including any child frames
263 static HTMLFormElementImpl *scanForForm(NodeImpl *start)
264 {
265     NodeImpl *n;
266     for (n = start; n; n = n->traverseNextNode()) {
267         NodeImpl::Id nodeID = idFromNode(n);
268         if (nodeID == ID_FORM) {
269             return static_cast<HTMLFormElementImpl *>(n);
270         } else if (n->isHTMLElement()
271                    && static_cast<HTMLElementImpl *>(n)->isGenericFormElement()) {
272             return static_cast<HTMLGenericFormElementImpl *>(n)->form();
273         } else if (nodeID == ID_FRAME || nodeID == ID_IFRAME) {
274             NodeImpl *childDoc = static_cast<HTMLFrameElementImpl *>(n)->contentDocument();
275             HTMLFormElementImpl *frameResult = scanForForm(childDoc);
276             if (frameResult) {
277                 return frameResult;
278             }
279         }
280     }
281     return 0;
282 }
283
284 // We look for either the form containing the current focus, or for one immediately after it
285 HTMLFormElementImpl *KWQKHTMLPart::currentForm() const
286 {
287     // start looking either at the active (first responder) node, or where the selection is
288     NodeImpl *start = activeNode().handle();
289     if (!start) {
290         start = selectionStart();
291     }
292
293     // try walking up the node tree to find a form element
294     NodeImpl *n;
295     for (n = start; n; n = n->parentNode()) {
296         if (idFromNode(n) == ID_FORM) {
297             return static_cast<HTMLFormElementImpl *>(n);
298         } else if (n->isHTMLElement()
299                    && static_cast<HTMLElementImpl *>(n)->isGenericFormElement()) {
300             return static_cast<HTMLGenericFormElementImpl *>(n)->form();
301         }
302     }
303
304     // try walking forward in the node tree to find a form element
305     if (!start) {
306         start = xmlDocImpl();
307     }
308     return scanForForm(start);
309 }
310
311 // Either get cached regexp or build one that matches any of the labels.
312 // The regexp we build is of the form:  (STR1|STR2|STRN)
313 QRegExp *regExpForLabels(NSArray *labels)
314 {
315     // All the ObjC calls in this method are simple array and string
316     // calls which we can assume do not raise exceptions
317
318
319     // Parallel arrays that we use to cache regExps.  In practice the number of expressions
320     // that the app will use is equal to the number of locales is used in searching.
321     static const unsigned int regExpCacheSize = 4;
322     static NSMutableArray *regExpLabels = nil;
323     static QPtrList <QRegExp> regExps;
324     static QRegExp wordRegExp = QRegExp("\\w");
325
326     QRegExp *result;
327     if (!regExpLabels) {
328         regExpLabels = [[NSMutableArray alloc] initWithCapacity:regExpCacheSize];
329     }
330     unsigned int cacheHit = [regExpLabels indexOfObject:labels];
331     if (cacheHit != NSNotFound) {
332         result = regExps.at(cacheHit);
333     } else {
334         QString pattern("(");
335         unsigned int numLabels = [labels count];
336         unsigned int i;
337         for (i = 0; i < numLabels; i++) {
338             QString label = QString::fromNSString([labels objectAtIndex:i]);
339
340             bool startsWithWordChar = false;
341             bool endsWithWordChar = false;
342             if (label.length() != 0) {
343                 startsWithWordChar = wordRegExp.search(label.at(0)) >= 0;
344                 endsWithWordChar = wordRegExp.search(label.at(label.length() - 1)) >= 0;
345             }
346             
347             if (i != 0) {
348                 pattern.append("|");
349             }
350             // Search for word boundaries only if label starts/ends with "word characters".
351             // If we always searched for word boundaries, this wouldn't work for languages
352             // such as Japanese.
353             if (startsWithWordChar) {
354                 pattern.append("\\b");
355             }
356             pattern.append(label);
357             if (endsWithWordChar) {
358                 pattern.append("\\b");
359             }
360         }
361         pattern.append(")");
362         result = new QRegExp(pattern, false);
363     }
364
365     // add regexp to the cache, making sure it is at the front for LRU ordering
366     if (cacheHit != 0) {
367         if (cacheHit != NSNotFound) {
368             // remove from old spot
369             [regExpLabels removeObjectAtIndex:cacheHit];
370             regExps.remove(cacheHit);
371         }
372         // add to start
373         [regExpLabels insertObject:labels atIndex:0];
374         regExps.insert(0, result);
375         // trim if too big
376         if ([regExpLabels count] > regExpCacheSize) {
377             [regExpLabels removeObjectAtIndex:regExpCacheSize];
378             QRegExp *last = regExps.last();
379             regExps.removeLast();
380             delete last;
381         }
382     }
383     return result;
384 }
385
386 NSString *KWQKHTMLPart::searchForLabelsAboveCell(QRegExp *regExp, HTMLTableCellElementImpl *cell)
387 {
388     RenderTableCell *cellRenderer = static_cast<RenderTableCell *>(cell->renderer());
389     RenderTableCell *cellAboveRenderer = cellRenderer->table()->cellAbove(cellRenderer);
390
391     if (cellAboveRenderer) {
392         HTMLTableCellElementImpl *aboveCell =
393             static_cast<HTMLTableCellElementImpl *>(cellAboveRenderer->element());
394
395         if (aboveCell) {
396             // search within the above cell we found for a match
397             for (NodeImpl *n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) {
398                 if (idFromNode(n) == ID_TEXT
399                     && n->renderer() && n->renderer()->style()->visibility() == VISIBLE)
400                 {
401                     // For each text chunk, run the regexp
402                     QString nodeString = n->nodeValue().string();
403                     int pos = regExp->searchRev(nodeString);
404                     if (pos >= 0) {
405                         return nodeString.mid(pos, regExp->matchedLength()).getNSString();
406                     }
407                 }
408             }
409         }
410     }
411     // Any reason in practice to search all cells in that are above cell?
412     return nil;
413 }
414
415 NSString *KWQKHTMLPart::searchForLabelsBeforeElement(NSArray *labels, ElementImpl *element)
416 {
417     QRegExp *regExp = regExpForLabels(labels);
418     // We stop searching after we've seen this many chars
419     const unsigned int charsSearchedThreshold = 500;
420     // This is the absolute max we search.  We allow a little more slop than
421     // charsSearchedThreshold, to make it more likely that we'll search whole nodes.
422     const unsigned int maxCharsSearched = 600;
423     // If the starting element is within a table, the cell that contains it
424     HTMLTableCellElementImpl *startingTableCell = 0;
425     bool searchedCellAbove = false;
426
427     // walk backwards in the node tree, until another element, or form, or end of tree
428     int unsigned lengthSearched = 0;
429     NodeImpl *n;
430     for (n = element->traversePreviousNode();
431          n && lengthSearched < charsSearchedThreshold;
432          n = n->traversePreviousNode())
433     {
434         NodeImpl::Id nodeID = idFromNode(n);
435         if (nodeID == ID_FORM
436             || (n->isHTMLElement()
437                 && static_cast<HTMLElementImpl *>(n)->isGenericFormElement()))
438         {
439             // We hit another form element or the start of the form - bail out
440             break;
441         } else if (nodeID == ID_TD && !startingTableCell) {
442             startingTableCell = static_cast<HTMLTableCellElementImpl *>(n);
443         } else if (nodeID == ID_TR && startingTableCell) {
444             NSString *result = searchForLabelsAboveCell(regExp, startingTableCell);
445             if (result) {
446                 return result;
447             }
448             searchedCellAbove = true;
449         } else if (nodeID == ID_TEXT
450                    && n->renderer() && n->renderer()->style()->visibility() == VISIBLE)
451         {
452             // For each text chunk, run the regexp
453             QString nodeString = n->nodeValue().string();
454             // add 100 for slop, to make it more likely that we'll search whole nodes
455             if (lengthSearched + nodeString.length() > maxCharsSearched) {
456                 nodeString = nodeString.right(charsSearchedThreshold - lengthSearched);
457             }
458             int pos = regExp->searchRev(nodeString);
459             if (pos >= 0) {
460                 return nodeString.mid(pos, regExp->matchedLength()).getNSString();
461             } else {
462                 lengthSearched += nodeString.length();
463             }
464         }
465     }
466
467     // If we started in a cell, but bailed because we found the start of the form or the
468     // previous element, we still might need to search the row above us for a label.
469     if (startingTableCell && !searchedCellAbove) {
470          return searchForLabelsAboveCell(regExp, startingTableCell);
471     } else {
472         return nil;
473     }
474 }
475
476 NSString *KWQKHTMLPart::matchLabelsAgainstElement(NSArray *labels, ElementImpl *element)
477 {
478     QString name = element->getAttribute(ATTR_NAME).string();
479     // Make numbers in field names behave like word boundaries, e.g., "address2"
480     name.replace(QRegExp("[[:digit:]]"), " ");
481     
482     QRegExp *regExp = regExpForLabels(labels);
483     // Use the largest match we can find in the whole name string
484     int pos;
485     int length;
486     int bestPos = -1;
487     int bestLength = -1;
488     int start = 0;
489     do {
490         pos = regExp->search(name, start);
491         if (pos != -1) {
492             length = regExp->matchedLength();
493             if (length >= bestLength) {
494                 bestPos = pos;
495                 bestLength = length;
496             }
497             start = pos+1;
498         }
499     } while (pos != -1);
500
501     if (bestPos != -1) {
502         return name.mid(bestPos, bestLength).getNSString();
503     } else {
504         return nil;
505     }
506 }
507
508 // Search from the end of the currently selected location if we are first responder, or from
509 // the beginning of the document if nothing is selected or we're not first responder.
510 bool KWQKHTMLPart::findString(NSString *string, bool forward, bool caseFlag, bool wrapFlag)
511 {
512     QString target = QString::fromNSString(string);
513     bool result;
514     // start on the correct edge of the selection, search to end
515     NodeImpl *selStart = selectionStart();
516     int selStartOffset = selectionStartOffset();
517     NodeImpl *selEnd = selectionEnd();
518     int selEndOffset = selectionEndOffset();
519     if (selStart) {
520         if (forward) {
521             // point to last char of selection, find will start right afterwards
522             findTextBegin(selEnd, selEndOffset-1);
523         } else {
524             // point to first char of selection, find will start right before
525             findTextBegin(selStart, selStartOffset);
526         }
527     } else {
528         findTextBegin();
529     }
530     result = findTextNext(target, forward, caseFlag, FALSE);
531     if (!result && wrapFlag) {
532         // start back at the other end, search the rest
533         findTextBegin();
534         result = findTextNext(target, forward, caseFlag, FALSE);
535         // if we got back to the same place we started, that doesn't count as success
536         if (result
537             && selStart == selectionStart()
538             && selStartOffset == selectionStartOffset())
539         {
540             result = false;
541         }
542     }
543
544     // khtml took care of moving the selection, but we need to move first responder too,
545     // so the selection is primary.  We also need to make the selection visible, since we
546     // cut the implementation of this in khtml_part.
547     if (result) {
548         jumpToSelection();
549     }
550     return result;
551 }
552
553 void KWQKHTMLPart::clearRecordedFormValues()
554 {
555     // It's safe to assume that our own classes and Foundation data
556     // structures won't raise exceptions in dealloc
557
558     [_formValuesAboutToBeSubmitted release];
559     _formValuesAboutToBeSubmitted = nil;
560     [_formAboutToBeSubmitted release];
561     _formAboutToBeSubmitted = nil;
562 }
563
564 void KWQKHTMLPart::recordFormValue(const QString &name, const QString &value, HTMLFormElementImpl *element)
565 {
566     // It's safe to assume that our own classes and basic Foundation
567     // data structures won't raise exceptions
568
569     if (!_formValuesAboutToBeSubmitted) {
570         _formValuesAboutToBeSubmitted = [[NSMutableDictionary alloc] init];
571         ASSERT(!_formAboutToBeSubmitted);
572         _formAboutToBeSubmitted = [[WebCoreDOMElement elementWithImpl:element] retain];
573     } else {
574         ASSERT([_formAboutToBeSubmitted elementImpl] == element);
575     }
576     [_formValuesAboutToBeSubmitted setObject:value.getNSString() forKey:name.getNSString()];
577 }
578
579 void KWQKHTMLPart::submitForm(const KURL &url, const URLArgs &args)
580 {
581     KWQ_BLOCK_NS_EXCEPTIONS;
582
583     // The form multi-submit logic here is only right when we are submitting a form that affects this frame.
584     // Eventually when we find a better fix we can remove this altogether.
585     WebCoreBridge *target = args.frameName.isEmpty() ? _bridge : [_bridge findFrameNamed:args.frameName.getNSString()];
586     KHTMLPart *targetPart = [target part];
587     bool willReplaceThisFrame = false;
588     for (KHTMLPart *p = this; p; p = p->parentPart()) {
589         if (p == targetPart) {
590             willReplaceThisFrame = true;
591             break;
592         }
593     }
594     if (willReplaceThisFrame) {
595         // We do not want to submit more than one form from the same page,
596         // nor do we want to submit a single form more than once.
597         // This flag prevents these from happening.
598         // Note that the flag is reset in setView()
599         // since this part may get reused if it is pulled from the b/f cache.
600         if (_submittedFormURL == url) {
601             return;
602         }
603         _submittedFormURL = url;
604     }
605
606     if (!args.doPost()) {
607         [_bridge loadURL:url.getNSURL()
608                 referrer:[_bridge referrer] 
609                   reload:args.reload
610              onLoadEvent:false
611                   target:args.frameName.getNSString()
612          triggeringEvent:_currentEvent
613                     form:_formAboutToBeSubmitted
614               formValues:_formValuesAboutToBeSubmitted];
615     } else {
616         ASSERT(args.contentType().startsWith("Content-Type: "));
617         [_bridge postWithURL:url.getNSURL()
618                     referrer:[_bridge referrer] 
619                       target:args.frameName.getNSString()
620                         data:[NSData dataWithBytes:args.postData.data() length:args.postData.size()]
621                  contentType:args.contentType().mid(14).getNSString()
622              triggeringEvent:_currentEvent
623                         form:_formAboutToBeSubmitted
624                   formValues:_formValuesAboutToBeSubmitted];
625     }
626     clearRecordedFormValues();
627
628     KWQ_UNBLOCK_NS_EXCEPTIONS;
629 }
630
631 void KWQKHTMLPart::setEncoding(const QString &name, bool userChosen)
632 {
633     if (!d->m_workingURL.isEmpty()) {
634         receivedFirstData();
635     }
636     d->m_encoding = name;
637     d->m_haveEncoding = userChosen;
638 }
639
640 void KWQKHTMLPart::addData(const char *bytes, int length)
641 {
642     ASSERT(d->m_workingURL.isEmpty());
643     ASSERT(d->m_doc);
644     ASSERT(d->m_doc->parsing());
645     write(bytes, length);
646 }
647
648 void KHTMLPart::frameDetached()
649 {
650     KWQ_BLOCK_NS_EXCEPTIONS;
651     [KWQ(this)->bridge() frameDetached];
652     KWQ_UNBLOCK_NS_EXCEPTIONS;
653
654     // FIXME: There may be a better place to do this that works for KHTML too.
655     FrameList& parentFrames = parentPart()->d->m_frames;
656     FrameIt end = parentFrames.end();
657     for (FrameIt it = parentFrames.begin(); it != end; ++it) {
658         if ((*it).m_part == this) {
659             parentFrames.remove(it);
660             deref();
661             break;
662         }
663     }
664 }
665
666 void KWQKHTMLPart::urlSelected(const KURL &url, int button, int state, const URLArgs &args)
667 {
668     KWQ_BLOCK_NS_EXCEPTIONS;
669     [_bridge loadURL:url.getNSURL()
670             referrer:[_bridge referrer]
671               reload:args.reload
672          onLoadEvent:false
673               target:args.frameName.getNSString()
674      triggeringEvent:_currentEvent
675                 form:nil
676           formValues:nil];
677     KWQ_UNBLOCK_NS_EXCEPTIONS;
678 }
679
680 class KWQPluginPart : public ReadOnlyPart
681 {
682     virtual bool openURL(const KURL &) { return true; }
683     virtual bool closeURL() { return true; }
684 };
685
686 ReadOnlyPart *KWQKHTMLPart::createPart(const ChildFrame &child, const KURL &url, const QString &mimeType)
687 {
688     ReadOnlyPart * volatile part = NULL;
689
690     KWQ_BLOCK_NS_EXCEPTIONS;
691
692     BOOL needFrame = [_bridge frameRequiredForMIMEType:mimeType.getNSString() URL:url.getNSURL()];
693     if (child.m_type == ChildFrame::Object && !needFrame) {
694         NSMutableArray *attributesArray = [NSMutableArray arrayWithCapacity:child.m_params.count()];
695         for (uint i = 0; i < child.m_params.count(); i++) {
696             [attributesArray addObject:child.m_params[i].getNSString()];
697         }
698         
699         KWQPluginPart *newPart = new KWQPluginPart;
700         newPart->setWidget(new QWidget([_bridge viewForPluginWithURL:url.getNSURL()
701                                                           attributes:attributesArray
702                                                              baseURL:KURL(d->m_doc->baseURL()).getNSURL()
703                                                             MIMEType:child.m_args.serviceType.getNSString()]));
704         part = newPart;
705     } else {
706         LOG(Frames, "name %s", child.m_name.ascii());
707         BOOL allowsScrolling = YES;
708         int marginWidth = -1;
709         int marginHeight = -1;
710         if (child.m_type != ChildFrame::Object) {
711             HTMLFrameElementImpl *o = static_cast<HTMLFrameElementImpl *>(child.m_frame->element());
712             allowsScrolling = o->scrollingMode() != QScrollView::AlwaysOff;
713             marginWidth = o->getMarginWidth();
714             marginHeight = o->getMarginHeight();
715         }
716         WebCoreBridge *childBridge = [_bridge createChildFrameNamed:child.m_name.getNSString()
717                                                             withURL:url.getNSURL()
718                                                          renderPart:child.m_frame
719                                                     allowsScrolling:allowsScrolling
720                                                         marginWidth:marginWidth
721                                                        marginHeight:marginHeight];
722         // This call needs to return an object with a ref, since the caller will expect to own it.
723         // childBridge owns the only ref so far.
724         [childBridge part]->ref();
725         part = [childBridge part];
726     }
727
728     KWQ_UNBLOCK_NS_EXCEPTIONS;
729
730     return part;
731 }
732     
733 void KWQKHTMLPart::setView(KHTMLView *view)
734 {
735     // Detach the document now, so any onUnload handlers get run - if
736     // we wait until the view is destroyed, then things won't be
737     // hooked up enough for some JavaScript calls to work.
738     if (d->m_doc && view == NULL) {
739         d->m_doc->detach();
740     }
741
742     if (view) {
743         view->ref();
744     }
745     if (d->m_view) {
746         d->m_view->deref();
747     }
748     d->m_view = view;
749     setWidget(view);
750     
751     // Only one form submission is allowed per view of a part.
752     // Since this part may be getting reused as a result of being
753     // pulled from the back/forward cache, reset this flag.
754     _submittedFormURL = KURL();
755 }
756
757 KHTMLView *KWQKHTMLPart::view() const
758 {
759     return d->m_view;
760 }
761
762 void KWQKHTMLPart::setTitle(const DOMString &title)
763 {
764     QString text = title.string();
765     text.replace('\\', backslashAsCurrencySymbol());
766
767     KWQ_BLOCK_NS_EXCEPTIONS;
768     [_bridge setTitle:text.getNSString()];
769     KWQ_UNBLOCK_NS_EXCEPTIONS;
770 }
771
772 void KWQKHTMLPart::setStatusBarText(const QString &status)
773 {
774     QString text = status;
775     text.replace('\\', backslashAsCurrencySymbol());
776
777     KWQ_BLOCK_NS_EXCEPTIONS;
778     [_bridge setStatusText:text.getNSString()];
779     KWQ_UNBLOCK_NS_EXCEPTIONS;
780 }
781
782 void KWQKHTMLPart::scheduleClose()
783 {
784     KWQ_BLOCK_NS_EXCEPTIONS;
785     [_bridge closeWindowSoon];
786     KWQ_UNBLOCK_NS_EXCEPTIONS;
787 }
788
789 void KWQKHTMLPart::unfocusWindow()
790 {
791     KWQ_BLOCK_NS_EXCEPTIONS;
792     [_bridge unfocusWindow];
793     KWQ_UNBLOCK_NS_EXCEPTIONS;
794 }
795
796 void KWQKHTMLPart::jumpToSelection()
797 {
798     // Assumes that selection will only ever be text nodes. This is currently
799     // true, but will it always be so?
800     if (!d->m_selectionStart.isNull()) {
801         RenderText *rt = dynamic_cast<RenderText *>(d->m_selectionStart.handle()->renderer());
802         if (rt) {
803             int x = 0, y = 0;
804             rt->posOfChar(d->m_startOffset, x, y);
805             // The -50 offset is copied from KHTMLPart::findTextNext, which sets the contents position
806             // after finding a matched text string.
807            d->m_view->setContentsPos(x - 50, y - 50);
808         }
809 /*
810         I think this would be a better way to do this, to avoid needless horizontal scrolling,
811         but it is not feasible until selectionRect() returns a tighter rect around the
812         selected text.  Right now it works at element granularity.
813  
814         NSView *docView = d->m_view->getDocumentView();
815
816         KWQ_BLOCK_NS_EXCEPTIONS;
817         NSRect selRect = NSRect(selectionRect());
818         NSRect visRect = [docView visibleRect];
819         if (!NSContainsRect(visRect, selRect)) {
820             // pad a bit so we overscroll slightly
821             selRect = NSInsetRect(selRect, -10.0, -10.0);
822             selRect = NSIntersectionRect(selRect, [docView bounds]);
823             [docView scrollRectToVisible:selRect];
824         }
825         KWQ_UNBLOCK_NS_EXCEPTIONS;
826 */
827     }
828 }
829
830 void KWQKHTMLPart::redirectionTimerStartedOrStopped()
831 {
832     // Don't report history navigations, just actual redirection.
833     if (d->m_scheduledRedirection == historyNavigationScheduled) {
834         return;
835     }
836     
837     KWQ_BLOCK_NS_EXCEPTIONS;
838     if (d->m_redirectionTimer.isActive()) {
839         [_bridge reportClientRedirectToURL:KURL(d->m_redirectURL).getNSURL()
840                                      delay:d->m_delayRedirect
841                                   fireDate:[d->m_redirectionTimer.getNSTimer() fireDate]
842                                lockHistory:d->m_redirectLockHistory
843                                isJavaScriptFormAction:d->m_executingJavaScriptFormAction];
844     } else {
845         [_bridge reportClientRedirectCancelled:d->m_cancelWithLoadInProgress];
846     }
847     KWQ_UNBLOCK_NS_EXCEPTIONS;
848 }
849
850 void KWQKHTMLPart::paint(QPainter *p, const QRect &rect)
851 {
852 #ifndef NDEBUG
853     bool isPrinting = (p->device()->devType() == QInternal::Printer);
854     if (!isPrinting && xmlDocImpl() && !xmlDocImpl()->ownerElement()) {
855         p->fillRect(rect.x(), rect.y(), rect.width(), rect.height(), QColor(0xFF, 0, 0));
856     }
857 #endif
858
859     if (renderer()) {
860         renderer()->layer()->paint(p, rect);
861     } else {
862         ERROR("called KWQKHTMLPart::paint with nil renderer");
863     }
864 }
865
866 void KWQKHTMLPart::paintSelectionOnly(QPainter *p, const QRect &rect)
867 {
868     if (renderer()) {
869         renderer()->layer()->paint(p, rect, true);
870     } else {
871         ERROR("called KWQKHTMLPart::paintSelectionOnly with nil renderer");
872     }
873 }
874
875 void KWQKHTMLPart::adjustPageHeight(float *newBottom, float oldTop, float oldBottom, float bottomLimit)
876 {
877     RenderCanvas *root = static_cast<RenderCanvas *>(xmlDocImpl()->renderer());
878     if (root) {
879         // Use a printer device, with painting disabled for the pagination phase
880         QPainter painter(true);
881         painter.setPaintingDisabled(true);
882
883         root->setTruncatedAt((int)floor(oldBottom));
884         QRect dirtyRect(0, (int)floor(oldTop),
885                         root->docWidth(), (int)ceil(oldBottom-oldTop));
886         root->layer()->paint(&painter, dirtyRect);
887         *newBottom = root->bestTruncatedAt();
888         if (*newBottom == 0) {
889             *newBottom = oldBottom;
890         }
891     } else {
892         *newBottom = oldBottom;
893     }
894 }
895
896 RenderObject *KWQKHTMLPart::renderer()
897 {
898     DocumentImpl *doc = xmlDocImpl();
899     return doc ? doc->renderer() : 0;
900 }
901
902 QString KWQKHTMLPart::userAgent() const
903 {
904     NSString * volatile us = nil;
905
906     KWQ_BLOCK_NS_EXCEPTIONS;
907     us = [_bridge userAgentForURL:m_url.getNSURL()];
908     KWQ_UNBLOCK_NS_EXCEPTIONS;
909          
910     return QString::fromNSString(us);
911 }
912
913 QString KWQKHTMLPart::mimeTypeForFileName(const QString &fileName) const
914 {
915     NSString *ns = fileName.getNSString();
916     NSString * volatile mimeType = @"";
917
918     KWQ_BLOCK_NS_EXCEPTIONS;
919     mimeType = [_bridge MIMETypeForPath:ns];
920     KWQ_UNBLOCK_NS_EXCEPTIONS;
921
922     return QString::fromNSString(mimeType);
923 }
924
925 NSView *KWQKHTMLPart::nextKeyViewInFrame(NodeImpl *node, KWQSelectionDirection direction)
926 {
927     DocumentImpl *doc = xmlDocImpl();
928     if (!doc) {
929         return nil;
930     }
931     for (;;) {
932         node = direction == KWQSelectingNext
933             ? doc->nextFocusNode(node) : doc->previousFocusNode(node);
934         if (!node) {
935             return nil;
936         }
937         RenderWidget *renderWidget = dynamic_cast<RenderWidget *>(node->renderer());
938         if (renderWidget) {
939             QWidget *widget = renderWidget->widget();
940             KHTMLView *childFrameWidget = dynamic_cast<KHTMLView *>(widget);
941             if (childFrameWidget) {
942                 NSView *view = KWQ(childFrameWidget->part())->nextKeyViewInFrame(0, direction);
943                 if (view) {
944                     return view;
945                 }
946             } else if (widget) {
947                 NSView *view = widget->getView();
948                 // AppKit won't be able to handle scrolling and making us the first responder
949                 // well unless we are actually installed in the correct place. KHTML only does
950                 // that for visible widgets, so we need to do it explicitly here.
951                 int x, y;
952                 if (view && renderWidget->absolutePosition(x, y)) {
953                     renderWidget->view()->addChild(widget, x, y);
954                     return view;
955                 }
956             }
957         }
958     }
959 }
960
961 NSView *KWQKHTMLPart::nextKeyViewInFrameHierarchy(NodeImpl *node, KWQSelectionDirection direction)
962 {
963     NSView *next = nextKeyViewInFrame(node, direction);
964     if (next) {
965         return next;
966     }
967     
968     KWQKHTMLPart *parent = KWQ(parentPart());
969     if (parent) {
970         next = parent->nextKeyView(parent->childFrame(this)->m_frame->element(), direction);
971         if (next) {
972             return next;
973         }
974     }
975     
976     return nil;
977 }
978
979 NSView *KWQKHTMLPart::nextKeyView(NodeImpl *node, KWQSelectionDirection direction)
980 {
981     NSView * volatile next = nextKeyViewInFrameHierarchy(node, direction);
982     if (next) {
983         return next;
984     }
985
986     // Look at views from the top level part up, looking for a next key view that we can use.
987
988     KWQ_BLOCK_NS_EXCEPTIONS;
989     next = direction == KWQSelectingNext
990         ? [_bridge nextKeyViewOutsideWebFrameViews]
991         : [_bridge previousKeyViewOutsideWebFrameViews];
992     KWQ_UNBLOCK_NS_EXCEPTIONS;
993
994     if (next) {
995         return next;
996     }
997     
998     // If all else fails, make a loop by starting from 0.
999     return nextKeyViewInFrameHierarchy(0, direction);
1000 }
1001
1002 NSView *KWQKHTMLPart::nextKeyViewForWidget(QWidget *startingWidget, KWQSelectionDirection direction)
1003 {
1004     // Use the event filter object to figure out which RenderWidget owns this QWidget and get to the DOM.
1005     // Then get the next key view in the order determined by the DOM.
1006     NodeImpl *node = nodeForWidget(startingWidget);
1007     ASSERT(node);
1008     return partForNode(node)->nextKeyView(node, direction);
1009 }
1010
1011 bool KWQKHTMLPart::currentEventIsMouseDownInWidget(QWidget *candidate)
1012 {
1013     volatile NSEventType eventType = (NSEventType)0;
1014
1015     KWQ_BLOCK_NS_EXCEPTIONS;
1016     eventType = [[NSApp currentEvent] type];
1017     KWQ_UNBLOCK_NS_EXCEPTIONS;
1018
1019     switch (eventType) {
1020         case NSLeftMouseDown:
1021         case NSRightMouseDown:
1022         case NSOtherMouseDown:
1023             break;
1024         default:
1025             return NO;
1026     }
1027
1028     NodeImpl *node = nodeForWidget(candidate);
1029     ASSERT(node);
1030     return partForNode(node)->nodeUnderMouse() == node;
1031 }
1032
1033
1034 QMap<int, ScheduledAction*> *KWQKHTMLPart::pauseActions(const void *key)
1035 {
1036     if (d->m_doc && d->m_jscript) {
1037         Window *w = Window::retrieveWindow(this);
1038         if (w && w->hasTimeouts()) {
1039             return w->pauseTimeouts(key);
1040         }
1041     }
1042     return 0;
1043 }
1044
1045 void KWQKHTMLPart::resumeActions(QMap<int, ScheduledAction*> *actions, const void *key)
1046 {
1047     if (d->m_doc && d->m_jscript && d->m_bJScriptEnabled) {
1048         Window *w = Window::retrieveWindow(this);
1049         if (w) {
1050             w->resumeTimeouts(actions, key);
1051         }
1052     }
1053 }
1054
1055 bool KWQKHTMLPart::canCachePage()
1056 {
1057     // Only save page state if:
1058     // 1.  We're not a frame or frameset.
1059     // 2.  The page has no unload handler.
1060     // 3.  The page has no password fields.
1061     // 4.  The URL for the page is https.
1062     if (d->m_frames.count() ||
1063         parentPart() ||
1064         m_url.protocol().startsWith("https") || 
1065         (d->m_doc && (d->m_doc->hasWindowEventListener(EventImpl::UNLOAD_EVENT) ||
1066                       d->m_doc->hasPasswordField()))) {
1067         return false;
1068     }
1069     return true;
1070 }
1071
1072 void KWQKHTMLPart::saveWindowProperties(SavedProperties *windowProperties)
1073 {
1074     Window *window = Window::retrieveWindow(this);
1075     if (window)
1076         window->saveProperties(*windowProperties);
1077 }
1078
1079 void KWQKHTMLPart::saveLocationProperties(SavedProperties *locationProperties)
1080 {
1081     Window *window = Window::retrieveWindow(this);
1082     if (window)
1083         window->location()->saveProperties(*locationProperties);
1084 }
1085
1086 void KWQKHTMLPart::restoreWindowProperties(SavedProperties *windowProperties)
1087 {
1088     Window *window = Window::retrieveWindow(this);
1089     if (window)
1090         window->restoreProperties(*windowProperties);
1091 }
1092
1093 void KWQKHTMLPart::restoreLocationProperties(SavedProperties *locationProperties)
1094 {
1095     Window *window = Window::retrieveWindow(this);
1096     if (window)
1097         window->location()->restoreProperties(*locationProperties);
1098 }
1099
1100 void KWQKHTMLPart::saveInterpreterBuiltins(SavedBuiltins &interpreterBuiltins)
1101 {
1102     if (jScript() && jScript()->interpreter()) {
1103         jScript()->interpreter()->saveBuiltins(interpreterBuiltins);
1104     }
1105 }
1106
1107 void KWQKHTMLPart::restoreInterpreterBuiltins(const SavedBuiltins &interpreterBuiltins)
1108 {
1109     if (jScript() && jScript()->interpreter()) {
1110         jScript()->interpreter()->restoreBuiltins(interpreterBuiltins);
1111     }
1112 }
1113
1114 void KWQKHTMLPart::openURLFromPageCache(KWQPageState *state)
1115 {
1116     // It's safe to assume none of the KWQPageState methods will raise
1117     // exceptions, since KWQPageState is implemented by WebCore and
1118     // does not throw
1119
1120     DocumentImpl *doc = [state document];
1121     RenderObject *renderer = [state renderer];
1122     KURL *url = [state URL];
1123     SavedProperties *windowProperties = [state windowProperties];
1124     SavedProperties *locationProperties = [state locationProperties];
1125     SavedBuiltins *interpreterBuiltins = [state interpreterBuiltins];
1126     QMap<int, ScheduledAction*> *actions = [state pausedActions];
1127     
1128     cancelRedirection();
1129
1130     // We still have to close the previous part page.
1131     if (!d->m_restored){
1132         closeURL();
1133     }
1134             
1135     d->m_bComplete = false;
1136     
1137     // Don't re-emit the load event.
1138     d->m_bLoadEventEmitted = true;
1139     
1140     // delete old status bar msg's from kjs (if it _was_ activated on last URL)
1141     if( d->m_bJScriptEnabled )
1142     {
1143         d->m_kjsStatusBarText = QString::null;
1144         d->m_kjsDefaultStatusBarText = QString::null;
1145     }
1146
1147     ASSERT (url);
1148     
1149     m_url = *url;
1150     
1151     // 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
1152     // data arrives) (Simon)
1153     if(m_url.protocol().startsWith( "http" ) && !m_url.host().isEmpty() && m_url.path().isEmpty()) {
1154         m_url.setPath("/");
1155         emit d->m_extension->setLocationBarURL( m_url.prettyURL() );
1156     }
1157     
1158     // copy to m_workingURL after fixing m_url above
1159     d->m_workingURL = m_url;
1160         
1161     emit started( 0L );
1162     
1163     // -----------begin-----------
1164     clear();
1165
1166     doc->restoreRenderer(renderer);
1167     
1168     d->m_bCleared = false;
1169     d->m_cacheId = 0;
1170     d->m_bComplete = false;
1171     d->m_bLoadEventEmitted = false;
1172     d->m_referrer = m_url.url();
1173     
1174     setView(doc->view());
1175     
1176     d->m_doc = doc;
1177     d->m_doc->ref();
1178     
1179     Decoder *decoder = doc->decoder();
1180     if (decoder) {
1181         decoder->ref();
1182     }
1183     if (d->m_decoder) {
1184         d->m_decoder->deref();
1185     }
1186     d->m_decoder = decoder;
1187
1188     updatePolicyBaseURL();
1189         
1190     restoreWindowProperties (windowProperties);
1191     restoreLocationProperties (locationProperties);
1192     restoreInterpreterBuiltins (*interpreterBuiltins);
1193
1194     if (actions)
1195         resumeActions (actions, state);
1196     
1197     checkCompleted();
1198 }
1199
1200 WebCoreBridge *KWQKHTMLPart::bridgeForWidget(const QWidget *widget)
1201 {
1202     ASSERT_ARG(widget, widget);
1203
1204     NodeImpl *node = nodeForWidget(widget);
1205     if (node) {
1206         return partForNode(node)->bridge() ;
1207     }
1208     
1209     // Assume all widgets are either form controls, or KHTMLViews.
1210     const KHTMLView *view = dynamic_cast<const KHTMLView *>(widget);
1211     ASSERT(view);
1212     return KWQ(view->part())->bridge();
1213 }
1214
1215 KWQKHTMLPart *KWQKHTMLPart::partForNode(NodeImpl *node)
1216 {
1217     ASSERT_ARG(node, node);
1218     return KWQ(node->getDocument()->view()->part());
1219 }
1220
1221 NodeImpl *KWQKHTMLPart::nodeForWidget(const QWidget *widget)
1222 {
1223     ASSERT_ARG(widget, widget);
1224     const QObject *o = widget->eventFilterObject();
1225     return o ? static_cast<const RenderWidget *>(o)->element() : 0;
1226 }
1227
1228 void KWQKHTMLPart::setDocumentFocus(QWidget *widget)
1229 {
1230     NodeImpl *node = nodeForWidget(widget);
1231     if (node) {
1232         node->getDocument()->setFocusNode(node);
1233     } else {
1234         ERROR("unable to clear focus because widget had no corresponding node");
1235     }
1236 }
1237
1238 void KWQKHTMLPart::clearDocumentFocus(QWidget *widget)
1239 {
1240     NodeImpl *node = nodeForWidget(widget);
1241     if (node) {
1242         node->getDocument()->setFocusNode(0);
1243     } else {
1244         ERROR("unable to clear focus because widget had no corresponding node");
1245     }
1246 }
1247
1248 void KWQKHTMLPart::saveDocumentState()
1249 {
1250     // Do not save doc state if the page has a password field and a form that would be submitted
1251     // via https
1252     if (!(d->m_doc && d->m_doc->hasPasswordField() && d->m_doc->hasSecureForm())) {
1253         KWQ_BLOCK_NS_EXCEPTIONS;
1254         [_bridge saveDocumentState];
1255         KWQ_UNBLOCK_NS_EXCEPTIONS;
1256     }
1257 }
1258
1259 void KWQKHTMLPart::restoreDocumentState()
1260 {
1261     KWQ_BLOCK_NS_EXCEPTIONS;
1262     [_bridge restoreDocumentState];
1263     KWQ_UNBLOCK_NS_EXCEPTIONS;
1264 }
1265
1266 QPtrList<KWQKHTMLPart> &KWQKHTMLPart::mutableInstances()
1267 {
1268     static QPtrList<KWQKHTMLPart> instancesList;
1269     return instancesList;
1270 }
1271
1272 void KWQKHTMLPart::updatePolicyBaseURL()
1273 {
1274     // FIXME: docImpl() returns null for everything other than HTML documents; is this causing problems? -dwh
1275     if (parentPart() && parentPart()->docImpl()) {
1276         setPolicyBaseURL(parentPart()->docImpl()->policyBaseURL());
1277     } else {
1278         setPolicyBaseURL(m_url.url());
1279     }
1280 }
1281
1282 void KWQKHTMLPart::setPolicyBaseURL(const DOMString &s)
1283 {
1284     // FIXME: XML documents will cause this to return null.  docImpl() is
1285     // an HTMLdocument only. -dwh
1286     if (docImpl())
1287         docImpl()->setPolicyBaseURL(s);
1288     ConstFrameIt end = d->m_frames.end();
1289     for (ConstFrameIt it = d->m_frames.begin(); it != end; ++it) {
1290         ReadOnlyPart *subpart = (*it).m_part;
1291         static_cast<KWQKHTMLPart *>(subpart)->setPolicyBaseURL(s);
1292     }
1293 }
1294
1295 QString KWQKHTMLPart::requestedURLString() const
1296 {
1297     NSString * volatile URL = @"";
1298
1299     KWQ_BLOCK_NS_EXCEPTIONS;
1300     URL = [_bridge requestedURLString];
1301     KWQ_UNBLOCK_NS_EXCEPTIONS;
1302
1303     return QString::fromNSString(URL);
1304 }
1305
1306 QString KWQKHTMLPart::incomingReferrer() const
1307 {
1308     NSString * volatile referrer = @"";
1309
1310     KWQ_BLOCK_NS_EXCEPTIONS;
1311     referrer = [_bridge incomingReferrer];
1312     KWQ_UNBLOCK_NS_EXCEPTIONS;
1313
1314     return QString::fromNSString(referrer);
1315 }
1316
1317 void KWQKHTMLPart::forceLayout()
1318 {
1319     KHTMLView *v = d->m_view;
1320     if (v) {
1321         v->layout();
1322         // We cannot unschedule a pending relayout, since the force can be called with
1323         // a tiny rectangle from a drawRect update.  By unscheduling we in effect
1324         // "validate" and stop the necessary full repaint from occurring.  Basically any basic
1325         // append/remove DHTML is broken by this call.  For now, I have removed the optimization
1326         // until we have a better invalidation stategy. -dwh
1327         //v->unscheduleRelayout();
1328     }
1329 }
1330
1331 void KWQKHTMLPart::forceLayoutForPageWidth(float pageWidth)
1332 {
1333     // Dumping externalRepresentation(_part->renderer()).ascii() is a good trick to see
1334     // the state of things before and after the layout
1335     RenderCanvas *root = static_cast<RenderCanvas *>(xmlDocImpl()->renderer());
1336     if (root) {
1337         // This magic is basically copied from khtmlview::print
1338         root->setWidth((int)ceil(pageWidth));
1339         root->setNeedsLayoutAndMinMaxRecalc();
1340         forceLayout();
1341     }
1342 }
1343
1344 void KWQKHTMLPart::sendResizeEvent()
1345 {
1346     KHTMLView *v = d->m_view;
1347     if (v) {
1348         // Sending an event can result in the destruction of the view and part.
1349         // We ref so that happens after we return from the KHTMLView function.
1350         v->ref();
1351         QResizeEvent e;
1352         v->resizeEvent(&e);
1353         v->deref();
1354     }
1355 }
1356
1357 void KWQKHTMLPart::runJavaScriptAlert(const QString &message)
1358 {
1359     QString text = message;
1360     text.replace('\\', backslashAsCurrencySymbol());
1361     KWQ_BLOCK_NS_EXCEPTIONS;
1362     [_bridge runJavaScriptAlertPanelWithMessage:text.getNSString()];
1363     KWQ_UNBLOCK_NS_EXCEPTIONS;
1364 }
1365
1366 bool KWQKHTMLPart::runJavaScriptConfirm(const QString &message)
1367 {
1368     QString text = message;
1369     text.replace('\\', backslashAsCurrencySymbol());
1370     volatile bool result = false;
1371
1372     KWQ_BLOCK_NS_EXCEPTIONS;
1373     result = [_bridge runJavaScriptConfirmPanelWithMessage:text.getNSString()];
1374     KWQ_UNBLOCK_NS_EXCEPTIONS;
1375
1376     return result;
1377 }
1378
1379 bool KWQKHTMLPart::runJavaScriptPrompt(const QString &prompt, const QString &defaultValue, QString &result)
1380 {
1381     QString promptText = prompt;
1382     promptText.replace('\\', backslashAsCurrencySymbol());
1383     QString defaultValueText = defaultValue;
1384     defaultValueText.replace('\\', backslashAsCurrencySymbol());
1385
1386     NSString * volatile returnedText = @"";
1387     volatile bool ok = false;
1388
1389     KWQ_BLOCK_NS_EXCEPTIONS;
1390     [_bridge runJavaScriptTextInputPanelWithPrompt:prompt.getNSString()
1391         defaultText:defaultValue.getNSString() returningText:(NSString **)&returnedText];
1392     KWQ_UNBLOCK_NS_EXCEPTIONS;
1393
1394     if (ok) {
1395         result = QString::fromNSString(returnedText);
1396         result.replace(backslashAsCurrencySymbol(), '\\');
1397     }
1398
1399     return ok;
1400 }
1401
1402 void KWQKHTMLPart::createEmptyDocument()
1403 {
1404     // Although it's not completely clear from the name of this function,
1405     // it does nothing if we already have a document, and just creates an
1406     // empty one if we have no document at all.
1407     if (!d->m_doc) {
1408         KWQ_BLOCK_NS_EXCEPTIONS;
1409         [_bridge loadEmptyDocumentSynchronously];
1410         KWQ_UNBLOCK_NS_EXCEPTIONS;
1411     }
1412 }
1413
1414 void KWQKHTMLPart::addMetaData(const QString &key, const QString &value)
1415 {
1416     d->m_job->addMetaData(key, value);
1417 }
1418
1419 bool KWQKHTMLPart::keyEvent(NSEvent *event)
1420 {
1421     volatile bool result = false;
1422
1423     KWQ_BLOCK_NS_EXCEPTIONS;
1424
1425     ASSERT([event type] == NSKeyDown || [event type] == NSKeyUp);
1426
1427     // Check for cases where we are too early for events -- possible unmatched key up
1428     // from pressing return in the location bar.
1429     DocumentImpl *doc = xmlDocImpl();
1430     if (!doc) {
1431         KWQ_UNBLOCK_RETURN_VALUE(false, bool);
1432     }
1433     NodeImpl *node = doc->focusNode();
1434     if (!node && docImpl()) {
1435         node = docImpl()->body();
1436     }
1437     if (!node) {
1438         KWQ_UNBLOCK_RETURN_VALUE(false, bool);
1439     }
1440     
1441     NSEvent *oldCurrentEvent = _currentEvent;
1442     _currentEvent = [event retain];
1443
1444     const char *characters = [[event characters] lossyCString];
1445     int ascii = (characters != nil && strlen(characters) == 1) ? characters[0] : 0;
1446
1447     QKeyEvent qEvent([event type] == NSKeyDown ? QEvent::KeyPress : QEvent::KeyRelease,
1448                      [event keyCode],
1449                      ascii,
1450                      stateForCurrentEvent(),
1451                      QString::fromNSString([event characters]),
1452                      [event isARepeat]);
1453     result = !node->dispatchKeyEvent(&qEvent);
1454
1455     // We want to send both a down and a press for the initial key event.
1456     // This is a temporary hack; we need to do this a better way.
1457     if ([event type] == NSKeyDown && ![event isARepeat]) {
1458         QKeyEvent qEvent(QEvent::KeyPress,
1459                          [event keyCode],
1460                          ascii,
1461                          stateForCurrentEvent(),
1462                          QString::fromNSString([event characters]),
1463                          true);
1464         if (!node->dispatchKeyEvent(&qEvent)) {
1465             result = true;
1466         }
1467     }
1468
1469     ASSERT(_currentEvent == event);
1470     [event release];
1471     _currentEvent = oldCurrentEvent;
1472
1473     KWQ_UNBLOCK_NS_EXCEPTIONS;
1474
1475     return result;
1476 }
1477
1478 // This does the same kind of work that KHTMLPart::openURL does, except it relies on the fact
1479 // that a higher level already checked that the URLs match and the scrolling is the right thing to do.
1480 void KWQKHTMLPart::scrollToAnchor(const KURL &URL)
1481 {
1482     cancelRedirection();
1483
1484     m_url = URL;
1485     started(0);
1486
1487     if (!gotoAnchor(URL.encodedHtmlRef()))
1488         gotoAnchor(URL.htmlRef());
1489
1490     d->m_bComplete = true;
1491     d->m_doc->setParsing(false);
1492
1493     completed();
1494 }
1495
1496 bool KWQKHTMLPart::closeURL()
1497 {
1498     saveDocumentState();
1499     return KHTMLPart::closeURL();
1500 }
1501
1502 void KWQKHTMLPart::khtmlMousePressEvent(MousePressEvent *event)
1503 {
1504     // If we got the event back, that must mean it wasn't prevented,
1505     // so it's allowed to start a drag or selection.
1506     _mouseDownMayStartDrag = true;
1507     _mouseDownMayStartSelect = true;
1508
1509     if (!passWidgetMouseDownEventToWidget(event)) {
1510         // We don't do this at the start of mouse down handling (before calling into WebCore),
1511         // because we don't want to do it until we know we didn't hit a widget.
1512         NSView *view = d->m_view->getDocumentView();
1513
1514         KWQ_BLOCK_NS_EXCEPTIONS;
1515         if ([_currentEvent clickCount] <= 1 && [_bridge firstResponder] != view) {
1516             [_bridge makeFirstResponder:view];
1517         }
1518         KWQ_UNBLOCK_NS_EXCEPTIONS;
1519
1520         KHTMLPart::khtmlMousePressEvent(event);
1521     }
1522 }
1523
1524 void KWQKHTMLPart::khtmlMouseDoubleClickEvent(MouseDoubleClickEvent *event)
1525 {
1526     if (!passWidgetMouseDownEventToWidget(event)) {
1527         KHTMLPart::khtmlMouseDoubleClickEvent(event);
1528     }
1529 }
1530
1531 bool KWQKHTMLPart::passWidgetMouseDownEventToWidget(khtml::MouseEvent *event)
1532 {
1533     // Figure out which view to send the event to.
1534     RenderObject *target = event->innerNode().handle() ? event->innerNode().handle()->renderer() : 0;
1535     if (!target)
1536         return false;
1537
1538     QWidget* widget = RenderLayer::gScrollBar;
1539     if (!widget) {
1540         if (!target->isWidget())
1541             return false;
1542         widget = static_cast<RenderWidget *>(target)->widget();
1543     }
1544
1545     // Doubleclick events don't exist in Cocoa.  Since passWidgetMouseDownEventToWidget will
1546     // just pass _currentEvent down to the widget,  we don't want to call it for events that
1547     // don't correspond to Cocoa events.  The mousedown/ups will have already been passed on as
1548     // part of the pressed/released handling.
1549     if (!MouseDoubleClickEvent::test(event))
1550         return passWidgetMouseDownEventToWidget(widget);
1551     else
1552         return true;
1553 }
1554
1555 bool KWQKHTMLPart::passWidgetMouseDownEventToWidget(RenderWidget *renderWidget)
1556 {
1557     return passWidgetMouseDownEventToWidget(renderWidget->widget());
1558 }
1559
1560 bool KWQKHTMLPart::passWidgetMouseDownEventToWidget(QWidget* widget)
1561 {
1562     // FIXME: this method always returns true
1563
1564     if (!widget) {
1565         ERROR("hit a RenderWidget without a corresponding QWidget, means a frame is half-constructed");
1566         return true;
1567     }
1568
1569     KWQ_BLOCK_NS_EXCEPTIONS;
1570     
1571     NSView *nodeView = widget->getView();
1572     ASSERT(nodeView);
1573     ASSERT([nodeView superview]);
1574     NSView *topView = nodeView;
1575     NSView *superview;
1576     while ((superview = [topView superview])) {
1577         topView = superview;
1578     }
1579     NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[_currentEvent locationInWindow] fromView:topView]];
1580     if (view == nil) {
1581         ERROR("KHTML says we hit a RenderWidget, but AppKit doesn't agree we hit the corresponding NSView");
1582         KWQ_UNBLOCK_RETURN_VALUE(true, bool);
1583     }
1584     
1585     if ([_bridge firstResponder] == view) {
1586         // In the case where we just became first responder, we should send the mouseDown:
1587         // to the NSTextField, not the NSTextField's editor. This code makes sure that happens.
1588         // If we don't do this, we see a flash of selected text when clicking in a text field.
1589         if (_firstResponderAtMouseDownTime != view && [view isKindOfClass:[NSTextView class]]) {
1590             NSView *superview = view;
1591             while (superview != nodeView) {
1592                 superview = [superview superview];
1593                 ASSERT(superview);
1594                 if ([superview isKindOfClass:[NSControl class]]) {
1595                     NSControl *control = superview;
1596                     if ([control currentEditor] == view) {
1597                         view = superview;
1598                     }
1599                     break;
1600                 }
1601             }
1602         }
1603     } else {
1604         // Normally [NSWindow sendEvent:] handles setting the first responder.
1605         // But in our case, the event was sent to the view representing the entire web page.
1606         if ([_currentEvent clickCount] <= 1 && [view acceptsFirstResponder] && [view needsPanelToBecomeKey]) {
1607             [_bridge makeFirstResponder:view];
1608         }
1609     }
1610
1611     ASSERT(!_sendingEventToSubview);
1612     _sendingEventToSubview = true;
1613     [view mouseDown:_currentEvent];
1614     _sendingEventToSubview = false;
1615     
1616     // Remember which view we sent the event to, so we can direct the release event properly.
1617     _mouseDownView = view;
1618     _mouseDownWasInSubframe = false;
1619
1620     KWQ_UNBLOCK_NS_EXCEPTIONS;
1621
1622     return true;
1623 }
1624
1625 bool KWQKHTMLPart::lastEventIsMouseUp()
1626 {
1627     // Many AK widgets run their own event loops and consume events while the mouse is down.
1628     // When they finish, currentEvent is the mouseUp that they exited on.  We need to update
1629     // the khtml state with this mouseUp, which khtml never saw.  This method lets us detect
1630     // that state.
1631     volatile bool result = false;
1632
1633     KWQ_BLOCK_NS_EXCEPTIONS;
1634     NSEvent *currentEventAfterHandlingMouseDown = [NSApp currentEvent];
1635     if (_currentEvent != currentEventAfterHandlingMouseDown) {
1636         if ([currentEventAfterHandlingMouseDown type] == NSLeftMouseUp) {
1637             result = true;
1638         }
1639     }
1640     KWQ_UNBLOCK_NS_EXCEPTIONS;
1641
1642     return result;
1643 }
1644     
1645 // Note that this does the same kind of check as [target isDescendantOf:superview].
1646 // There are two differences: This is a lot slower because it has to walk the whole
1647 // tree, and this works in cases where the target has already been deallocated.
1648 static bool findViewInSubviews(NSView *superview, NSView *target)
1649 {
1650     volatile bool result = false;
1651     
1652     KWQ_BLOCK_NS_EXCEPTIONS;
1653     NSEnumerator *e = [[superview subviews] objectEnumerator];
1654     NSView *subview;
1655     while ((subview = [e nextObject])) {
1656         if (subview == target || findViewInSubviews(subview, target)) {
1657             result = true;
1658         }
1659     }
1660     KWQ_UNBLOCK_NS_EXCEPTIONS;
1661     
1662     return result;
1663 }
1664
1665 NSView *KWQKHTMLPart::mouseDownViewIfStillGood()
1666 {
1667     // Since we have no way of tracking the lifetime of _mouseDownView, we have to assume that
1668     // it could be deallocated already. We search for it in our subview tree; if we don't find
1669     // it, we set it to nil.
1670     NSView *mouseDownView = _mouseDownView;
1671     if (!mouseDownView) {
1672         return nil;
1673     }
1674     KHTMLView *topKHTMLView = d->m_view;
1675     NSView *topView = topKHTMLView ? topKHTMLView->getView() : nil;
1676     if (!topView || !findViewInSubviews(topView, mouseDownView)) {
1677         _mouseDownView = nil;
1678         return nil;
1679     }
1680     return mouseDownView;
1681 }
1682
1683 void KWQKHTMLPart::khtmlMouseMoveEvent(MouseMoveEvent *event)
1684 {
1685     KWQ_BLOCK_NS_EXCEPTIONS;
1686
1687     if ([_currentEvent type] == NSLeftMouseDragged) {
1688         NSView *view = mouseDownViewIfStillGood();
1689
1690         if (view) {
1691             _sendingEventToSubview = true;
1692             [view mouseDragged:_currentEvent];
1693             _sendingEventToSubview = false;
1694             KWQ_UNBLOCK_RETURN;
1695         }
1696
1697         if (_mouseDownMayStartDrag &&
1698             !d->m_selectionInitiatedWithDoubleClick &&
1699             !d->m_selectionInitiatedWithTripleClick &&
1700             [_bridge mayStartDragWithMouseDragged:_currentEvent])
1701         {
1702             // We are starting a text/image/url drag, so the cursor should be an arrow
1703             d->m_view->resetCursor();
1704             [_bridge handleMouseDragged:_currentEvent];
1705             KWQ_UNBLOCK_RETURN;
1706         } else if (_mouseDownMayStartSelect) {
1707             // we use khtml's selection but our own autoscrolling
1708             [_bridge handleAutoscrollForMouseDragged:_currentEvent];
1709             // Don't allow dragging after we've started selecting.
1710             _mouseDownMayStartDrag = false;
1711         } else {
1712             KWQ_UNBLOCK_RETURN;
1713         }
1714     } else {
1715         // If we allowed the other side of the bridge to handle a drag
1716         // last time, then m_bMousePressed might still be set. So we
1717         // clear it now to make sure the next move after a drag
1718         // doesn't look like a drag.
1719         d->m_bMousePressed = false;
1720     }
1721
1722     KHTMLPart::khtmlMouseMoveEvent(event);
1723
1724     KWQ_UNBLOCK_NS_EXCEPTIONS;
1725 }
1726
1727 void KWQKHTMLPart::khtmlMouseReleaseEvent(MouseReleaseEvent *event)
1728 {
1729     NSView *view = mouseDownViewIfStillGood();
1730     if (!view) {
1731         KHTMLPart::khtmlMouseReleaseEvent(event);
1732         return;
1733     }
1734     
1735     _sendingEventToSubview = true;
1736     KWQ_BLOCK_NS_EXCEPTIONS;
1737     [view mouseUp:_currentEvent];
1738     KWQ_UNBLOCK_NS_EXCEPTIONS;
1739     _sendingEventToSubview = false;
1740 }
1741
1742 void KWQKHTMLPart::clearTimers(KHTMLView *view)
1743 {
1744     if (view)
1745         view->unscheduleRelayout();
1746 }
1747
1748 void KWQKHTMLPart::clearTimers()
1749 {
1750     clearTimers(d->m_view);
1751 }
1752
1753 bool KWQKHTMLPart::passSubframeEventToSubframe(NodeImpl::MouseEvent &event)
1754 {
1755     volatile NSEventType eventType = (NSEventType)0;
1756
1757     KWQ_BLOCK_NS_EXCEPTIONS;
1758     eventType = [_currentEvent type];
1759     KWQ_UNBLOCK_NS_EXCEPTIONS;
1760
1761     switch (eventType) {
1762         case NSLeftMouseDown: {
1763             NodeImpl *node = event.innerNode.handle();
1764             if (!node) {
1765                 return false;
1766             }
1767             RenderPart *renderPart = dynamic_cast<RenderPart *>(node->renderer());
1768             if (!renderPart) {
1769                 return false;
1770             }
1771             if (!passWidgetMouseDownEventToWidget(renderPart)) {
1772                 return false;
1773             }
1774             _mouseDownWasInSubframe = true;
1775             return true;
1776         }
1777         case NSLeftMouseUp: {
1778             if (!_mouseDownWasInSubframe) {
1779                 return false;
1780             }
1781             NSView *view = mouseDownViewIfStillGood();
1782             if (!view) {
1783                 return false;
1784             }
1785             ASSERT(!_sendingEventToSubview);
1786             _sendingEventToSubview = true;
1787             KWQ_BLOCK_NS_EXCEPTIONS;
1788             [view mouseUp:_currentEvent];
1789             KWQ_UNBLOCK_NS_EXCEPTIONS;
1790             _sendingEventToSubview = false;
1791             return true;
1792         }
1793         case NSLeftMouseDragged: {
1794             if (!_mouseDownWasInSubframe) {
1795                 return false;
1796             }
1797             NSView *view = mouseDownViewIfStillGood();
1798             if (!view) {
1799                 return false;
1800             }
1801             ASSERT(!_sendingEventToSubview);
1802             _sendingEventToSubview = true;
1803             KWQ_BLOCK_NS_EXCEPTIONS;
1804             [view mouseDragged:_currentEvent];
1805             KWQ_UNBLOCK_NS_EXCEPTIONS;
1806             _sendingEventToSubview = false;
1807             return true;
1808         }
1809         default:
1810             return false;
1811     }
1812 }
1813
1814 int KWQKHTMLPart::buttonForCurrentEvent()
1815 {
1816     volatile NSEventType eventType = (NSEventType)0;
1817
1818     KWQ_BLOCK_NS_EXCEPTIONS;
1819     eventType = [_currentEvent type];
1820     KWQ_UNBLOCK_NS_EXCEPTIONS;
1821     
1822     switch (eventType) {
1823     case NSLeftMouseDown:
1824     case NSLeftMouseUp:
1825         return Qt::LeftButton;
1826     case NSRightMouseDown:
1827     case NSRightMouseUp:
1828         return Qt::RightButton;
1829     case NSOtherMouseDown:
1830     case NSOtherMouseUp:
1831         return Qt::MidButton;
1832     default:
1833         return 0;
1834     }
1835 }
1836
1837 int KWQKHTMLPart::stateForCurrentEvent()
1838 {
1839     volatile int state = buttonForCurrentEvent();
1840     
1841     volatile unsigned modifiers = 0;
1842
1843     KWQ_BLOCK_NS_EXCEPTIONS;
1844     modifiers = [_currentEvent modifierFlags];
1845     KWQ_UNBLOCK_NS_EXCEPTIONS;
1846
1847     if (modifiers & NSControlKeyMask)
1848         state |= Qt::ControlButton;
1849     if (modifiers & NSShiftKeyMask)
1850         state |= Qt::ShiftButton;
1851     if (modifiers & NSAlternateKeyMask)
1852         state |= Qt::AltButton;
1853     if (modifiers & NSCommandKeyMask)
1854         state |= Qt::MetaButton;
1855     if (modifiers & NSNumericPadKeyMask)
1856         state |= Qt::Keypad;
1857     
1858     return state;
1859 }
1860
1861 void KWQKHTMLPart::mouseDown(NSEvent *event)
1862 {
1863     KHTMLView *v = d->m_view;
1864     if (!v || _sendingEventToSubview) {
1865         return;
1866     }
1867
1868     KWQ_BLOCK_NS_EXCEPTIONS;
1869
1870     _mouseDownView = nil;
1871
1872     NSEvent *oldCurrentEvent = _currentEvent;
1873     _currentEvent = [event retain];
1874     
1875     NSResponder *oldFirstResponderAtMouseDownTime = _firstResponderAtMouseDownTime;
1876     // Unlike other places in WebCore where we get the first
1877     // responder, in this case we must be talking about the real first
1878     // responder, so we could just ask the bridge's window, instead of
1879     // the bridge. It's unclear which is better.
1880     _firstResponderAtMouseDownTime = [[_bridge firstResponder] retain];
1881
1882     _mouseDownMayStartDrag = false;
1883     _mouseDownMayStartSelect = false;
1884
1885     // Sending an event can result in the destruction of the view and part.
1886     // We ref so that happens after we return from the KHTMLView function.
1887     v->ref();
1888     QMouseEvent kEvent(QEvent::MouseButtonPress, QPoint([event locationInWindow]),
1889         buttonForCurrentEvent(), stateForCurrentEvent(), [event clickCount]);
1890     v->viewportMousePressEvent(&kEvent);
1891     v->deref();
1892     
1893     [_firstResponderAtMouseDownTime release];
1894     _firstResponderAtMouseDownTime = oldFirstResponderAtMouseDownTime;
1895
1896     ASSERT(_currentEvent == event);
1897     [event release];
1898     _currentEvent = oldCurrentEvent;
1899
1900     KWQ_UNBLOCK_NS_EXCEPTIONS;
1901 }
1902
1903 void KWQKHTMLPart::mouseDragged(NSEvent *event)
1904 {
1905     KHTMLView *v = d->m_view;
1906     if (!v || _sendingEventToSubview) {
1907         return;
1908     }
1909
1910     KWQ_BLOCK_NS_EXCEPTIONS;
1911
1912     NSEvent *oldCurrentEvent = _currentEvent;
1913     _currentEvent = [event retain];
1914
1915     // Sending an event can result in the destruction of the view and part.
1916     // We ref so that happens after we return from the KHTMLView function.
1917     v->ref();
1918     QMouseEvent kEvent(QEvent::MouseMove, QPoint([event locationInWindow]), Qt::LeftButton, Qt::LeftButton);
1919     v->viewportMouseMoveEvent(&kEvent);
1920     v->deref();
1921     
1922     ASSERT(_currentEvent == event);
1923     [event release];
1924     _currentEvent = oldCurrentEvent;
1925
1926     KWQ_UNBLOCK_NS_EXCEPTIONS;
1927 }
1928
1929 void KWQKHTMLPart::mouseUp(NSEvent *event)
1930 {
1931     KHTMLView *v = d->m_view;
1932     if (!v || _sendingEventToSubview) {
1933         return;
1934     }
1935
1936     KWQ_BLOCK_NS_EXCEPTIONS;
1937
1938     NSEvent *oldCurrentEvent = _currentEvent;
1939     _currentEvent = [event retain];
1940
1941     // Sending an event can result in the destruction of the view and part.
1942     // We ref so that happens after we return from the KHTMLView function.
1943     v->ref();
1944     // Our behavior here is a little different that Qt. Qt always sends
1945     // a mouse release event, even for a double click. To correct problems
1946     // in khtml's DOM click event handling we do not send a release here
1947     // for a double click. Instead we send that event from KHTMLView's
1948     // viewportMouseDoubleClickEvent. Note also that the third click of
1949     // a triple click is treated as a single click, but the fourth is then
1950     // treated as another double click. Hence the "% 2" below.
1951     int clickCount = [event clickCount];
1952     if (clickCount > 0 && clickCount % 2 == 0) {
1953         QMouseEvent doubleClickEvent(QEvent::MouseButtonDblClick, QPoint([event locationInWindow]),
1954             buttonForCurrentEvent(), stateForCurrentEvent(), clickCount);
1955         v->viewportMouseDoubleClickEvent(&doubleClickEvent);
1956     } else {
1957         QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPoint([event locationInWindow]),
1958             buttonForCurrentEvent(), stateForCurrentEvent(), clickCount);
1959         v->viewportMouseReleaseEvent(&releaseEvent);
1960     }
1961     v->deref();
1962     
1963     ASSERT(_currentEvent == event);
1964     [event release];
1965     _currentEvent = oldCurrentEvent;
1966     
1967     _mouseDownView = nil;
1968
1969     KWQ_UNBLOCK_NS_EXCEPTIONS;
1970 }
1971
1972 /*
1973  A hack for the benefit of AK's PopUpButton, which uses the Carbon menu manager, which thus
1974  eats all subsequent events after it is starts its modal tracking loop.  After the interaction
1975  is done, this routine is used to fix things up.  When a mouse down started us tracking in
1976  the widget, we post a fake mouse up to balance the mouse down we started with. When a 
1977  key down started us tracking in the widget, we post a fake key up to balance things out.
1978  In addition, we post a fake mouseMoved to get the cursor in sync with whatever we happen to 
1979  be over after the tracking is done.
1980  */
1981 void KWQKHTMLPart::sendFakeEventsAfterWidgetTracking(NSEvent *initiatingEvent)
1982 {
1983     KWQ_BLOCK_NS_EXCEPTIONS;
1984
1985     _sendingEventToSubview = false;
1986     int eventType = [initiatingEvent type];
1987     ASSERT(eventType == NSLeftMouseDown || eventType == NSKeyDown);
1988     NSEvent *fakeEvent = nil;
1989     if (eventType == NSLeftMouseDown) {
1990         fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
1991                                 location:[initiatingEvent locationInWindow]
1992                             modifierFlags:[initiatingEvent modifierFlags]
1993                                 timestamp:[initiatingEvent timestamp]
1994                             windowNumber:[initiatingEvent windowNumber]
1995                                     context:[initiatingEvent context]
1996                                 eventNumber:[initiatingEvent eventNumber]
1997                                 clickCount:[initiatingEvent clickCount]
1998                                 pressure:[initiatingEvent pressure]];
1999     
2000         mouseUp(fakeEvent);
2001     }
2002     else { // eventType == NSKeyDown
2003         fakeEvent = [NSEvent keyEventWithType:NSKeyUp
2004                                 location:[initiatingEvent locationInWindow]
2005                            modifierFlags:[initiatingEvent modifierFlags]
2006                                timestamp:[initiatingEvent timestamp]
2007                             windowNumber:[initiatingEvent windowNumber]
2008                                  context:[initiatingEvent context]
2009                               characters:[initiatingEvent characters] 
2010              charactersIgnoringModifiers:[initiatingEvent charactersIgnoringModifiers] 
2011                                isARepeat:[initiatingEvent isARepeat] 
2012                                  keyCode:[initiatingEvent keyCode]];
2013         keyEvent(fakeEvent);
2014     }
2015     // FIXME:  We should really get the current modifierFlags here, but there's no way to poll
2016     // them in Cocoa, and because the event stream was stolen by the Carbon menu code we have
2017     // no up-to-date cache of them anywhere.
2018     fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
2019                                    location:[[_bridge window] convertScreenToBase:[NSEvent mouseLocation]]
2020                               modifierFlags:[initiatingEvent modifierFlags]
2021                                   timestamp:[initiatingEvent timestamp]
2022                                windowNumber:[initiatingEvent windowNumber]
2023                                     context:[initiatingEvent context]
2024                                 eventNumber:0
2025                                  clickCount:0
2026                                    pressure:0];
2027     mouseMoved(fakeEvent);
2028
2029     KWQ_UNBLOCK_NS_EXCEPTIONS;
2030 }
2031
2032 void KWQKHTMLPart::mouseMoved(NSEvent *event)
2033 {
2034     KHTMLView *v = d->m_view;
2035     // Reject a mouse moved if the button is down - screws up tracking during autoscroll
2036     // These happen because WebKit sometimes has to fake up moved events.
2037     if (!v || d->m_bMousePressed) {
2038         return;
2039     }
2040     
2041     KWQ_BLOCK_NS_EXCEPTIONS;
2042
2043     NSEvent *oldCurrentEvent = _currentEvent;
2044     _currentEvent = [event retain];
2045     
2046     // Sending an event can result in the destruction of the view and part.
2047     // We ref so that happens after we return from the KHTMLView function.
2048     v->ref();
2049     QMouseEvent kEvent(QEvent::MouseMove, QPoint([event locationInWindow]), 0, stateForCurrentEvent());
2050     v->viewportMouseMoveEvent(&kEvent);
2051     v->deref();
2052     
2053     ASSERT(_currentEvent == event);
2054     [event release];
2055     _currentEvent = oldCurrentEvent;
2056
2057     KWQ_UNBLOCK_NS_EXCEPTIONS;
2058 }
2059
2060 bool KWQKHTMLPart::sendContextMenuEvent(NSEvent *event)
2061 {
2062     DocumentImpl *doc = d->m_doc;
2063     KHTMLView *v = d->m_view;
2064     if (!doc || !v) {
2065         return false;
2066     }
2067
2068     volatile bool swallowEvent = false;
2069
2070     KWQ_BLOCK_NS_EXCEPTIONS;
2071
2072     NSEvent *oldCurrentEvent = _currentEvent;
2073     _currentEvent = [event retain];
2074     
2075     QMouseEvent qev(QEvent::MouseButtonPress, QPoint([event locationInWindow]),
2076         buttonForCurrentEvent(), stateForCurrentEvent(), [event clickCount]);
2077
2078     int xm, ym;
2079     v->viewportToContents(qev.x(), qev.y(), xm, ym);
2080
2081     NodeImpl::MouseEvent mev(qev.stateAfter(), NodeImpl::MousePress);
2082     doc->prepareMouseEvent(false, xm, ym, &mev);
2083
2084     // Sending an event can result in the destruction of the view and part.
2085     // We ref so that happens after we return from the KHTMLView function.
2086     v->ref();
2087     swallowEvent = v->dispatchMouseEvent(EventImpl::CONTEXTMENU_EVENT,
2088         mev.innerNode.handle(), true, 0, &qev, true, NodeImpl::MousePress);
2089     v->deref();
2090
2091     ASSERT(_currentEvent == event);
2092     [event release];
2093     _currentEvent = oldCurrentEvent;
2094
2095     KWQ_UNBLOCK_NS_EXCEPTIONS;
2096
2097     return swallowEvent;
2098 }
2099
2100 struct ListItemInfo {
2101     unsigned start;
2102     unsigned end;
2103 };
2104
2105 static NSFileWrapper *fileWrapperForElement(ElementImpl *e)
2106 {
2107     RenderImage *renderer = static_cast<RenderImage *>(e->renderer());
2108     NSImage *image = renderer->pixmap().image();
2109
2110     NSFileWrapper * volatile wrapper = nil;
2111     
2112     KWQ_BLOCK_NS_EXCEPTIONS;
2113     NSData *tiffData = [image TIFFRepresentationUsingCompression:NSTIFFCompressionLZW factor:0.0];
2114
2115     wrapper = [[NSFileWrapper alloc] initRegularFileWithContents:tiffData];
2116     [wrapper setPreferredFilename:@"image.tiff"];
2117     [wrapper autorelease];
2118     KWQ_UNBLOCK_NS_EXCEPTIONS;
2119
2120     return wrapper;
2121 }
2122
2123 static ElementImpl *listParent(ElementImpl *item)
2124 {
2125     // Ick!  Avoid use of item->id() which confuses ObjC++.
2126     unsigned short _id = Node(item).elementId();
2127     
2128     while (_id != ID_UL && _id != ID_OL) {
2129         item = static_cast<ElementImpl *>(item->parentNode());
2130         if (!item)
2131             break;
2132         _id = Node(item).elementId();
2133     }
2134     return item;
2135 }
2136
2137 static NodeImpl* isTextFirstInListItem(NodeImpl *e)
2138 {
2139     if (Node(e).nodeType() != Node::TEXT_NODE)
2140         return 0;
2141     NodeImpl* par = e->parentNode();
2142     while (par) {
2143         if (par->firstChild() != e)
2144             return 0;
2145         if (Node(par).elementId() == ID_LI)
2146             return par;
2147         e = par;
2148         par = par->parentNode();
2149     }
2150     return 0;
2151 }
2152
2153 #define BULLET_CHAR 0x2022
2154 #define SQUARE_CHAR 0x25AA
2155 #define CIRCLE_CHAR 0x25E6
2156
2157 NSAttributedString *KWQKHTMLPart::attributedString(NodeImpl *_start, int startOffset, NodeImpl *endNode, int endOffset)
2158 {
2159     NodeImpl * volatile _startNode = _start;
2160
2161     if (_startNode == nil) {
2162         return nil;
2163     }
2164
2165     // This allocation and autorelease won't raise so it's OK to do it
2166     // outside the exception block
2167     NSMutableAttributedString * volatile result = [[[NSMutableAttributedString alloc] init] autorelease];
2168
2169     KWQ_BLOCK_NS_EXCEPTIONS;
2170
2171     bool hasNewLine = true;
2172     bool addedSpace = true;
2173     bool hasParagraphBreak = true;
2174     const ElementImpl *linkStartNode = 0;
2175     unsigned linkStartLocation = 0;
2176     QPtrList<ElementImpl> listItems;
2177     QValueList<ListItemInfo> listItemLocations;
2178     float maxMarkerWidth = 0;
2179     
2180     Node n = _startNode;
2181     
2182     // If the first item is the entire text of a list item, use the list item node as the start of the 
2183     // selection, not the text node.  The user's intent was probably to select the list.
2184     if (n.nodeType() == Node::TEXT_NODE && startOffset == 0) {
2185         NodeImpl *startListNode = isTextFirstInListItem(_startNode);
2186         if (startListNode){
2187             _startNode = startListNode;
2188             n = _startNode;
2189         }
2190     }
2191     
2192     while (!n.isNull()) {
2193         RenderObject *renderer = n.handle()->renderer();
2194         if (renderer) {
2195             RenderStyle *style = renderer->style();
2196             NSFont *font = style->font().getNSFont();
2197             if (n.nodeType() == Node::TEXT_NODE) {
2198                 if (hasNewLine) {
2199                     addedSpace = true;
2200                     hasNewLine = false;
2201                 }
2202                 QString text;
2203                 QString str = n.nodeValue().string();
2204                 int start = (n == _startNode) ? startOffset : -1;
2205                 int end = (n == endNode) ? endOffset : -1;
2206                 if (renderer->isText()) {
2207                     if (renderer->style()->whiteSpace() == PRE) {
2208                         int runStart = (start == -1) ? 0 : start;
2209                         int runEnd = (end == -1) ? str.length() : end;
2210                         text += str.mid(runStart, runEnd-runStart);
2211                         addedSpace = str[runEnd-1].direction() == QChar::DirWS;
2212                     }
2213                     else {
2214                         RenderText* textObj = static_cast<RenderText*>(renderer);
2215                         InlineTextBoxArray runs = textObj->inlineTextBoxes();
2216                         if (runs.count() == 0 && str.length() > 0 && !addedSpace) {
2217                             // We have no runs, but we do have a length.  This means we must be
2218                             // whitespace that collapsed away at the end of a line.
2219                             text += " ";
2220                             addedSpace = true;
2221                         }
2222                         else {
2223                             addedSpace = false;
2224                             for (unsigned i = 0; i < runs.count(); i++) {
2225                                 int runStart = (start == -1) ? runs[i]->m_start : start;
2226                                 int runEnd = (end == -1) ? runs[i]->m_start + runs[i]->m_len : end;
2227                                 runEnd = QMIN(runEnd, runs[i]->m_start + runs[i]->m_len);
2228                                 bool spaceBetweenRuns = false;
2229                                 if (runStart >= runs[i]->m_start &&
2230                                     runStart < runs[i]->m_start + runs[i]->m_len) {
2231                                     QString runText = str.mid(runStart, runEnd - runStart);
2232                                     runText.replace('\n', ' ');
2233                                     text += runText;
2234                                     start = -1;
2235                                     spaceBetweenRuns = i+1 < runs.count() && runs[i+1]->m_start > runEnd;
2236                                     addedSpace = str[runEnd-1].direction() == QChar::DirWS;
2237                                 }
2238                                 if (end != -1 && runEnd >= end)
2239                                     break;
2240
2241                                 if (spaceBetweenRuns && !addedSpace) {
2242                                     text += " ";
2243                                     addedSpace = true;
2244                                 }
2245                             }
2246                         }
2247                     }
2248                 }
2249                 
2250                 text.replace('\\', renderer->backslashAsCurrencySymbol());
2251     
2252                 if (text.length() > 0) {
2253                     hasParagraphBreak = false;
2254                     NSMutableDictionary *attrs;
2255
2256                     attrs = [[NSMutableDictionary alloc] init];
2257                     [attrs setObject:font forKey:NSFontAttributeName];
2258                     if (style && style->color().isValid())
2259                         [attrs setObject:style->color().getNSColor() forKey:NSForegroundColorAttributeName];
2260                     if (style && style->backgroundColor().isValid())
2261                         [attrs setObject:style->backgroundColor().getNSColor() forKey:NSBackgroundColorAttributeName];
2262
2263                     NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString() attributes:attrs];
2264                     [attrs release];
2265                     [result appendAttributedString: partialString];                
2266                     [partialString release];
2267                 }
2268             } else {
2269                 // This is our simple HTML -> ASCII transformation:
2270                 QString text;
2271                 unsigned short _id = n.elementId();
2272                 switch(_id) {
2273                     case ID_A:
2274                         // Note the start of the <a> element.  We will add the NSLinkAttributeName
2275                         // attribute to the attributed string when navigating to the next sibling 
2276                         // of this node.
2277                         linkStartLocation = [result length];
2278                         linkStartNode = static_cast<ElementImpl*>(n.handle());
2279                         break;
2280
2281                     case ID_BR:
2282                         text += "\n";
2283                         hasNewLine = true;
2284                         break;
2285     
2286                     case ID_LI:
2287                         {
2288                             QString listText;
2289                             ElementImpl *itemParent = listParent(static_cast<ElementImpl *>(n.handle()));
2290                             
2291                             if (!hasNewLine)
2292                                 listText += '\n';
2293                             hasNewLine = true;
2294     
2295                             listItems.append(static_cast<ElementImpl*>(n.handle()));
2296                             ListItemInfo info;
2297                             info.start = [result length];
2298                             info.end = 0;
2299                             listItemLocations.append (info);
2300                             
2301                             listText += '\t';
2302                             if (itemParent){
2303                                 // Ick!  Avoid use of itemParent->id() which confuses ObjC++.
2304                                 khtml::RenderListItem *listRenderer = static_cast<khtml::RenderListItem*>(renderer);
2305
2306                                 maxMarkerWidth = MAX([font pointSize], maxMarkerWidth);
2307                                 switch(listRenderer->style()->listStyleType()) {
2308                                     case khtml::DISC:
2309                                         listText += ((QChar)BULLET_CHAR);
2310                                         break;
2311                                     case khtml::CIRCLE:
2312                                         listText += ((QChar)CIRCLE_CHAR);
2313                                         break;
2314                                     case khtml::SQUARE:
2315                                         listText += ((QChar)SQUARE_CHAR);
2316                                         break;
2317                                     case khtml::LNONE:
2318                                         break;
2319                                     default:
2320                                         QString marker = listRenderer->markerStringValue();
2321                                         listText += marker;
2322                                         // Use AppKit metrics.  Will be rendered by AppKit.
2323                                         float markerWidth = [font widthOfString: marker.getNSString()];
2324                                         maxMarkerWidth = MAX(markerWidth, maxMarkerWidth);
2325                                 }
2326
2327                                 listText += ' ';
2328                                 listText += '\t';
2329     
2330                                 NSMutableDictionary *attrs;
2331             
2332                                 attrs = [[NSMutableDictionary alloc] init];
2333                                 [attrs setObject:font forKey:NSFontAttributeName];
2334                                 if (style && style->color().isValid())
2335                                     [attrs setObject:style->color().getNSColor() forKey:NSForegroundColorAttributeName];
2336                                 if (style && style->backgroundColor().isValid())
2337                                     [attrs setObject:style->backgroundColor().getNSColor() forKey:NSBackgroundColorAttributeName];
2338             
2339                                 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:listText.getNSString() attributes:attrs];
2340                                 [attrs release];
2341                                 [result appendAttributedString: partialString];                
2342                                 [partialString release];
2343                             }
2344                         }
2345                         break;
2346
2347                     case ID_OL:
2348                     case ID_UL:
2349                         if (!hasNewLine)
2350                             text += "\n";
2351                         hasNewLine = true;
2352                         break;
2353
2354                     case ID_TD:
2355                     case ID_TH:
2356                     case ID_HR:
2357                     case ID_DD:
2358                     case ID_DL:
2359                     case ID_DT:
2360                     case ID_PRE:
2361                     case ID_BLOCKQUOTE:
2362                     case ID_DIV:
2363                         if (!hasNewLine)
2364                             text += '\n';
2365                         hasNewLine = true;
2366                         break;
2367                     case ID_P:
2368                     case ID_TR:
2369                     case ID_H1:
2370                     case ID_H2:
2371                     case ID_H3:
2372                     case ID_H4:
2373                     case ID_H5:
2374                     case ID_H6:
2375                         if (!hasNewLine)
2376                             text += '\n';
2377                         if (!hasParagraphBreak) {
2378                             text += '\n';
2379                             hasParagraphBreak = true;
2380                         }
2381                         hasNewLine = true;
2382                         break;
2383                         
2384                     case ID_IMG:
2385                         NSFileWrapper *fileWrapper = fileWrapperForElement(static_cast<ElementImpl *>(n.handle()));
2386                         NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWrapper:fileWrapper];
2387                         NSAttributedString *iString = [NSAttributedString attributedStringWithAttachment:attachment];
2388                         [result appendAttributedString: iString];
2389                         [attachment release];
2390                         break;
2391                 }
2392                 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()];
2393                 [result appendAttributedString: partialString];
2394                 [partialString release];
2395             }
2396         }
2397
2398         if (n == endNode)
2399             break;
2400
2401         Node next = n.firstChild();
2402         if (next.isNull()){
2403             next = n.nextSibling();
2404         }
2405
2406         while (next.isNull() && !n.parentNode().isNull()) {
2407             QString text;
2408             n = n.parentNode();
2409             if (n == endNode)
2410                 break;
2411             next = n.nextSibling();
2412
2413             unsigned short _id = n.elementId();
2414             switch(_id) {
2415                 case ID_A:
2416                     // End of a <a> element.  Create an attributed string NSLinkAttributeName attribute
2417                     // for the range of the link.  Note that we create the attributed string from the DOM, which
2418                     // will have corrected any illegally nested <a> elements.
2419                     if (linkStartNode && n.handle() == linkStartNode){
2420                         DOMString href = parseURL(linkStartNode->getAttribute(ATTR_HREF));
2421                         KURL kURL = KWQ(linkStartNode->getDocument()->view()->part())->completeURL(href.string());
2422                         
2423                         NSURL *URL = kURL.getNSURL();
2424                         [result addAttribute:NSLinkAttributeName value:URL range:NSMakeRange(linkStartLocation, [result length]-linkStartLocation)];
2425                         linkStartNode = 0;
2426                     }
2427                     break;
2428                 
2429                 case ID_OL:
2430                 case ID_UL:
2431                     if (!hasNewLine)
2432                         text += '\n';
2433                     hasNewLine = true;
2434                     break;
2435
2436                 case ID_LI:
2437                     {
2438                         int i, count = listItems.count();
2439                         for (i = 0; i < count; i++){
2440                             if (listItems.at(i) == n.handle()){
2441                                 listItemLocations[i].end = [result length];
2442                                 break;
2443                             }
2444                         }
2445                     }
2446                     if (!hasNewLine)
2447                         text += '\n';
2448                     hasNewLine = true;
2449                     break;
2450
2451                 case ID_TD:
2452                 case ID_TH:
2453                 case ID_HR:
2454                 case ID_DD:
2455                 case ID_DL:
2456                 case ID_DT:
2457                 case ID_PRE:
2458                 case ID_BLOCKQUOTE:
2459                 case ID_DIV:
2460                     if (!hasNewLine)
2461                         text += '\n';
2462                     hasNewLine = true;
2463                     break;
2464                 case ID_P:
2465                 case ID_TR:
2466                 case ID_H1:
2467                 case ID_H2:
2468                 case ID_H3:
2469                 case ID_H4:
2470                 case ID_H5:
2471                 case ID_H6:
2472                     if (!hasNewLine)
2473                         text += '\n';
2474                     // An extra newline is needed at the start, not the end, of these types of tags,
2475                     // so don't add another here.
2476                     hasNewLine = true;
2477                     break;
2478             }
2479             
2480             NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()];
2481             [result appendAttributedString:partialString];
2482             [partialString release];
2483         }
2484
2485         n = next;
2486     }
2487     
2488     // Apply paragraph styles from outside in.  This ensures that nested lists correctly
2489     // override their parent's paragraph style.
2490     {
2491         unsigned i, count = listItems.count();
2492         ElementImpl *e;
2493         ListItemInfo info;
2494
2495 #ifdef POSITION_LIST
2496         NodeImpl *containingBlock;
2497         int containingBlockX, containingBlockY;
2498         
2499         // Determine the position of the outermost containing block.  All paragraph
2500         // styles and tabs should be relative to this position.  So, the horizontal position of 
2501         // each item in the list (in the resulting attributed string) will be relative to position 
2502         // of the outermost containing block.
2503         if (count > 0){
2504             containingBlock = _startNode;
2505             while (containingBlock->renderer()->isInline()){
2506                 containingBlock = containingBlock->parentNode();
2507             }
2508             containingBlock->renderer()->absolutePosition(containingBlockX, containingBlockY);
2509         }
2510 #endif
2511         
2512         for (i = 0; i < count; i++){
2513             e = listItems.at(i);
2514             info = listItemLocations[i];
2515             
2516             if (info.end < info.start)
2517                 info.end = [result length];
2518                 
2519             RenderObject *r = e->renderer();
2520             RenderStyle *style = r->style();
2521
2522             int rx;
2523             NSFont *font = style->font().getNSFont();
2524             float pointSize = [font pointSize];
2525
2526 #ifdef POSITION_LIST
2527             int ry;
2528             r->absolutePosition(rx, ry);
2529             rx -= containingBlockX;
2530             
2531             // Ensure that the text is indented at least enough to allow for the markers.
2532             rx = MAX(rx, (int)maxMarkerWidth);
2533 #else
2534             rx = (int)MAX(maxMarkerWidth, pointSize);
2535 #endif
2536
2537             // The bullet text will be right aligned at the first tab marker, followed
2538             // by a space, followed by the list item text.  The space is arbitrarily
2539             // picked as pointSize*2/3.  The space on the first line of the text item
2540             // is established by a left aligned tab, on subsequent lines it's established
2541             // by the head indent.
2542             NSMutableParagraphStyle *mps = [[NSMutableParagraphStyle alloc] init];
2543             [mps setFirstLineHeadIndent: 0];
2544             [mps setHeadIndent: rx];
2545             [mps setTabStops:[NSArray arrayWithObjects:
2546                         [[[NSTextTab alloc] initWithType:NSRightTabStopType location:rx-(pointSize*2/3)] autorelease],
2547                         [[[NSTextTab alloc] initWithType:NSLeftTabStopType location:rx] autorelease],
2548                         nil]];
2549             [result addAttribute:NSParagraphStyleAttributeName value:mps range:NSMakeRange(info.start, info.end-info.start)];
2550             [mps release];
2551         }
2552     }
2553
2554     KWQ_UNBLOCK_NS_EXCEPTIONS;
2555
2556     //NSLog (@"%@", result);
2557     return result;
2558 }
2559
2560 QRect KWQKHTMLPart::selectionRect() const
2561 {
2562     if(!xmlDocImpl()){
2563         return QRect();
2564     }
2565
2566     RenderCanvas *root = static_cast<RenderCanvas *>(xmlDocImpl()->renderer());
2567     if (!root) {
2568         return QRect();
2569
2570     }
2571
2572     return root->selectionRect();
2573 }
2574
2575 KWQWindowWidget *KWQKHTMLPart::topLevelWidget()
2576 {
2577     return _windowWidget;
2578 }
2579
2580 int KWQKHTMLPart::selectionStartOffset() const
2581 {
2582     return d->m_startOffset;
2583 }
2584
2585 int KWQKHTMLPart::selectionEndOffset() const
2586 {
2587     return d->m_endOffset;
2588 }
2589
2590 NodeImpl *KWQKHTMLPart::selectionStart() const
2591 {
2592     return d->m_selectionStart.handle();
2593 }
2594
2595 NodeImpl *KWQKHTMLPart::selectionEnd() const
2596 {
2597     return d->m_selectionEnd.handle();
2598 }
2599
2600 void KWQKHTMLPart::setBridge(WebCoreBridge *p)
2601
2602     if (_bridge != p) {
2603         delete _windowWidget;
2604     }
2605     _bridge = p;
2606     _windowWidget = new KWQWindowWidget(_bridge);
2607 }
2608
2609 void KWQKHTMLPart::setMediaType(const QString &type)
2610 {
2611     if (d->m_view) {
2612         d->m_view->setMediaType(type);
2613     }
2614 }
2615
2616 QChar KWQKHTMLPart::backslashAsCurrencySymbol() const
2617 {
2618     DocumentImpl *doc = xmlDocImpl();
2619     if (!doc) {
2620         return '\\';
2621     }
2622     Decoder *decoder = doc->decoder();
2623     if (!decoder) {
2624         return '\\';
2625     }
2626     const QTextCodec *codec = decoder->codec();
2627     if (!codec) {
2628         return '\\';
2629     }
2630     return codec->backslashAsCurrencySymbol();
2631 }
2632
2633 NSColor *KWQKHTMLPart::bodyBackgroundColor(void) const
2634 {
2635     HTMLDocumentImpl *doc = docImpl();
2636     
2637     if (doc){
2638         HTMLElementImpl *body = doc->body();
2639         QColor bgColor =  body->renderer()->style()->backgroundColor();
2640         
2641         if (bgColor.isValid())
2642             return bgColor.getNSColor();
2643     }
2644     return nil;
2645 }
2646
2647 WebCoreKeyboardUIMode KWQKHTMLPart::keyboardUIMode() const
2648 {
2649     volatile WebCoreKeyboardUIMode mode = WebCoreDefaultKeyboardAccess;
2650
2651     KWQ_BLOCK_NS_EXCEPTIONS;
2652     mode = [_bridge keyboardUIMode];
2653     KWQ_UNBLOCK_NS_EXCEPTIONS;
2654
2655     return mode;
2656 }