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