2 * Copyright (C) 2004 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 #import "KWQKHTMLPart.h"
28 #import "DOMInternal.h"
29 #import "KWQClipboard.h"
30 #import "KWQDOMNode.h"
31 #import "KWQDummyView.h"
32 #import "KWQEditCommand.h"
33 #import "KWQExceptions.h"
34 #import "KWQKJobClasses.h"
35 #import "KWQLogging.h"
36 #import "KWQPageState.h"
37 #import "KWQPrinter.h"
38 #import "KWQScrollBar.h"
39 #import "KWQWindowWidget.h"
40 #import "KWQFoundationExtras.h"
41 #import "WebCoreBridge.h"
42 #import "WebCoreGraphicsBridge.h"
43 #import "WebCoreViewFactory.h"
45 #import "dom2_eventsimpl.h"
46 #import "dom2_rangeimpl.h"
47 #import "dom_position.h"
48 #import "dom_selection.h"
49 #import "dom_textimpl.h"
50 #import "html_documentimpl.h"
53 #import "htmltokenizer.h"
54 #import "khtml_text_operations.h"
55 #import "khtmlpart_p.h"
57 #import "kjs_binding.h"
58 #import "kjs_window.h"
59 #import "render_canvas.h"
60 #import "render_frames.h"
61 #import "render_image.h"
62 #import "render_list.h"
63 #import "render_style.h"
64 #import "render_table.h"
65 #import "render_text.h"
66 #import <JavaScriptCore/identifier.h>
67 #import <JavaScriptCore/property_map.h>
68 #import <JavaScriptCore/runtime.h>
69 #import <JavaScriptCore/runtime_root.h>
70 #import <JavaScriptCore/WebScriptObjectPrivate.h>
74 using DOM::AtomicString;
75 using DOM::ClipboardEventImpl;
76 using DOM::DocumentImpl;
78 using DOM::ElementImpl;
80 using DOM::HTMLDocumentImpl;
81 using DOM::HTMLElementImpl;
82 using DOM::HTMLFormElementImpl;
83 using DOM::HTMLFrameElementImpl;
84 using DOM::HTMLGenericFormElementImpl;
85 using DOM::HTMLTableCellElementImpl;
95 using khtml::CharacterIterator;
96 using khtml::ChildFrame;
98 using khtml::findPlainText;
99 using khtml::InlineTextBox;
100 using khtml::MouseDoubleClickEvent;
101 using khtml::MouseMoveEvent;
102 using khtml::MousePressEvent;
103 using khtml::MouseReleaseEvent;
104 using khtml::parseURL;
106 using khtml::RenderCanvas;
107 using khtml::RenderImage;
108 using khtml::RenderLayer;
109 using khtml::RenderListItem;
110 using khtml::RenderObject;
111 using khtml::RenderPart;
112 using khtml::RenderStyle;
113 using khtml::RenderTableCell;
114 using khtml::RenderText;
115 using khtml::RenderWidget;
116 using khtml::VISIBLE;
117 using khtml::WordAwareIterator;
121 using KJS::Interpreter;
123 using KJS::SavedBuiltins;
124 using KJS::SavedProperties;
125 using KJS::ScheduledAction;
128 using KJS::Bindings::Instance;
130 using KParts::ReadOnlyPart;
131 using KParts::URLArgs;
133 NSEvent *KWQKHTMLPart::_currentEvent = nil;
134 NSResponder *KWQKHTMLPart::_firstResponderAtMouseDownTime = nil;
136 void KHTMLPart::completed()
138 KWQ(this)->_completed.call();
141 void KHTMLPart::completed(bool arg)
143 KWQ(this)->_completed.call(arg);
146 void KHTMLPart::nodeActivated(const Node &)
150 bool KHTMLPart::openURL(const KURL &URL)
152 ASSERT_NOT_REACHED();
156 void KHTMLPart::onURL(const QString &)
160 void KHTMLPart::setStatusBarText(const QString &status)
162 KWQ(this)->setStatusBarText(status);
165 void KHTMLPart::started(Job *j)
167 KWQ(this)->_started.call(j);
170 static void redirectionTimerMonitor(void *context)
172 KWQKHTMLPart *kwq = static_cast<KWQKHTMLPart *>(context);
173 kwq->redirectionTimerStartedOrStopped();
176 KWQKHTMLPart::KWQKHTMLPart()
177 : _started(this, SIGNAL(started(KIO::Job *)))
178 , _completed(this, SIGNAL(completed()))
179 , _completedWithBool(this, SIGNAL(completed(bool)))
180 , _mouseDownView(nil)
181 , _sendingEventToSubview(false)
182 , _mouseDownMayStartDrag(false)
183 , _mouseDownMayStartSelect(false)
184 , _activationEventNumber(0)
185 , _formValuesAboutToBeSubmitted(nil)
186 , _formAboutToBeSubmitted(nil)
187 , _windowWidget(NULL)
188 , _displaysWithFocusAttributes(false)
189 , _drawSelectionOnly(false)
191 , _windowScriptObject(0)
192 , _windowScriptNPObject(0)
197 // Must init the cache before connecting to any signals
200 // The widget is made outside this class in our case.
201 KHTMLPart::init( 0, DefaultGUI );
203 mutableInstances().prepend(this);
204 d->m_redirectionTimer.setMonitor(redirectionTimerMonitor, this);
207 KWQKHTMLPart::~KWQKHTMLPart()
209 cleanupPluginRootObjects();
211 mutableInstances().remove(this);
216 // these are all basic Foundation classes and our own classes - we
217 // know they will not raise in dealloc, so no need to block
219 KWQRelease(_formValuesAboutToBeSubmitted);
220 KWQRelease(_formAboutToBeSubmitted);
222 KWQRelease(_windowScriptObject);
224 delete _windowWidget;
227 void KWQKHTMLPart::freeClipboard()
229 if (_dragClipboard) {
230 _dragClipboard->setAccessPolicy(KWQClipboard::Numb);
231 _dragClipboard->deref();
236 void KWQKHTMLPart::setSettings (KHTMLSettings *settings)
238 d->m_settings = settings;
241 QString KWQKHTMLPart::generateFrameName()
243 KWQ_BLOCK_EXCEPTIONS;
244 return QString::fromNSString([_bridge generateFrameName]);
245 KWQ_UNBLOCK_EXCEPTIONS;
250 void KWQKHTMLPart::provisionalLoadStarted()
252 // we don't want to wait until we get an actual http response back
253 // to cancel pending redirects, otherwise they might fire before
255 cancelRedirection(true);
258 bool KWQKHTMLPart::openURL(const KURL &url)
260 KWQ_BLOCK_EXCEPTIONS;
262 bool userGesture = true;
264 if (jScript() && jScript()->interpreter()) {
265 KHTMLPart *rootPart = this;
266 while (rootPart->parentPart() != 0)
267 rootPart = rootPart->parentPart();
268 KJS::ScriptInterpreter *interpreter = static_cast<KJS::ScriptInterpreter *>(KJSProxy::proxy(rootPart)->interpreter());
269 userGesture = interpreter->wasRunByUserGesture();
272 // FIXME: The lack of args here to get the reload flag from
273 // indicates a problem in how we use KHTMLPart::processObjectRequest,
274 // where we are opening the URL before the args are set up.
275 [_bridge loadURL:url.getNSURL()
276 referrer:[_bridge referrer]
278 userGesture:userGesture
284 KWQ_UNBLOCK_EXCEPTIONS;
289 void KWQKHTMLPart::openURLRequest(const KURL &url, const URLArgs &args)
291 KWQ_BLOCK_EXCEPTIONS;
293 [_bridge loadURL:url.getNSURL()
294 referrer:[_bridge referrer]
297 target:args.frameName.getNSString()
302 KWQ_UNBLOCK_EXCEPTIONS;
305 void KWQKHTMLPart::didNotOpenURL(const KURL &URL)
307 if (_submittedFormURL == URL) {
308 _submittedFormURL = KURL();
312 // Scans logically forward from "start", including any child frames
313 static HTMLFormElementImpl *scanForForm(NodeImpl *start)
316 for (n = start; n; n = n->traverseNextNode()) {
317 NodeImpl::Id nodeID = idFromNode(n);
318 if (nodeID == ID_FORM) {
319 return static_cast<HTMLFormElementImpl *>(n);
320 } else if (n->isHTMLElement()
321 && static_cast<HTMLElementImpl *>(n)->isGenericFormElement()) {
322 return static_cast<HTMLGenericFormElementImpl *>(n)->form();
323 } else if (nodeID == ID_FRAME || nodeID == ID_IFRAME) {
324 NodeImpl *childDoc = static_cast<HTMLFrameElementImpl *>(n)->contentDocument();
325 HTMLFormElementImpl *frameResult = scanForForm(childDoc);
334 // We look for either the form containing the current focus, or for one immediately after it
335 HTMLFormElementImpl *KWQKHTMLPart::currentForm() const
337 // start looking either at the active (first responder) node, or where the selection is
338 NodeImpl *start = activeNode().handle();
340 start = selectionStart();
343 // try walking up the node tree to find a form element
345 for (n = start; n; n = n->parentNode()) {
346 if (idFromNode(n) == ID_FORM) {
347 return static_cast<HTMLFormElementImpl *>(n);
348 } else if (n->isHTMLElement()
349 && static_cast<HTMLElementImpl *>(n)->isGenericFormElement()) {
350 return static_cast<HTMLGenericFormElementImpl *>(n)->form();
354 // try walking forward in the node tree to find a form element
356 start = xmlDocImpl();
358 return scanForForm(start);
361 // Either get cached regexp or build one that matches any of the labels.
362 // The regexp we build is of the form: (STR1|STR2|STRN)
363 QRegExp *regExpForLabels(NSArray *labels)
365 // All the ObjC calls in this method are simple array and string
366 // calls which we can assume do not raise exceptions
369 // Parallel arrays that we use to cache regExps. In practice the number of expressions
370 // that the app will use is equal to the number of locales is used in searching.
371 static const unsigned int regExpCacheSize = 4;
372 static NSMutableArray *regExpLabels = nil;
373 static QPtrList <QRegExp> regExps;
374 static QRegExp wordRegExp = QRegExp("\\w");
378 regExpLabels = [[NSMutableArray alloc] initWithCapacity:regExpCacheSize];
380 unsigned int cacheHit = [regExpLabels indexOfObject:labels];
381 if (cacheHit != NSNotFound) {
382 result = regExps.at(cacheHit);
384 QString pattern("(");
385 unsigned int numLabels = [labels count];
387 for (i = 0; i < numLabels; i++) {
388 QString label = QString::fromNSString([labels objectAtIndex:i]);
390 bool startsWithWordChar = false;
391 bool endsWithWordChar = false;
392 if (label.length() != 0) {
393 startsWithWordChar = wordRegExp.search(label.at(0)) >= 0;
394 endsWithWordChar = wordRegExp.search(label.at(label.length() - 1)) >= 0;
400 // Search for word boundaries only if label starts/ends with "word characters".
401 // If we always searched for word boundaries, this wouldn't work for languages
403 if (startsWithWordChar) {
404 pattern.append("\\b");
406 pattern.append(label);
407 if (endsWithWordChar) {
408 pattern.append("\\b");
412 result = new QRegExp(pattern, false);
415 // add regexp to the cache, making sure it is at the front for LRU ordering
417 if (cacheHit != NSNotFound) {
418 // remove from old spot
419 [regExpLabels removeObjectAtIndex:cacheHit];
420 regExps.remove(cacheHit);
423 [regExpLabels insertObject:labels atIndex:0];
424 regExps.insert(0, result);
426 if ([regExpLabels count] > regExpCacheSize) {
427 [regExpLabels removeObjectAtIndex:regExpCacheSize];
428 QRegExp *last = regExps.last();
429 regExps.removeLast();
436 NSString *KWQKHTMLPart::searchForLabelsAboveCell(QRegExp *regExp, HTMLTableCellElementImpl *cell)
438 RenderTableCell *cellRenderer = static_cast<RenderTableCell *>(cell->renderer());
439 RenderTableCell *cellAboveRenderer = cellRenderer->table()->cellAbove(cellRenderer);
441 if (cellAboveRenderer) {
442 HTMLTableCellElementImpl *aboveCell =
443 static_cast<HTMLTableCellElementImpl *>(cellAboveRenderer->element());
446 // search within the above cell we found for a match
447 for (NodeImpl *n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) {
448 if (idFromNode(n) == ID_TEXT
449 && n->renderer() && n->renderer()->style()->visibility() == VISIBLE)
451 // For each text chunk, run the regexp
452 QString nodeString = n->nodeValue().string();
453 int pos = regExp->searchRev(nodeString);
455 return nodeString.mid(pos, regExp->matchedLength()).getNSString();
461 // Any reason in practice to search all cells in that are above cell?
465 NSString *KWQKHTMLPart::searchForLabelsBeforeElement(NSArray *labels, ElementImpl *element)
467 QRegExp *regExp = regExpForLabels(labels);
468 // We stop searching after we've seen this many chars
469 const unsigned int charsSearchedThreshold = 500;
470 // This is the absolute max we search. We allow a little more slop than
471 // charsSearchedThreshold, to make it more likely that we'll search whole nodes.
472 const unsigned int maxCharsSearched = 600;
473 // If the starting element is within a table, the cell that contains it
474 HTMLTableCellElementImpl *startingTableCell = 0;
475 bool searchedCellAbove = false;
477 // walk backwards in the node tree, until another element, or form, or end of tree
478 int unsigned lengthSearched = 0;
480 for (n = element->traversePreviousNode();
481 n && lengthSearched < charsSearchedThreshold;
482 n = n->traversePreviousNode())
484 NodeImpl::Id nodeID = idFromNode(n);
485 if (nodeID == ID_FORM
486 || (n->isHTMLElement()
487 && static_cast<HTMLElementImpl *>(n)->isGenericFormElement()))
489 // We hit another form element or the start of the form - bail out
491 } else if (nodeID == ID_TD && !startingTableCell) {
492 startingTableCell = static_cast<HTMLTableCellElementImpl *>(n);
493 } else if (nodeID == ID_TR && startingTableCell) {
494 NSString *result = searchForLabelsAboveCell(regExp, startingTableCell);
498 searchedCellAbove = true;
499 } else if (nodeID == ID_TEXT
500 && n->renderer() && n->renderer()->style()->visibility() == VISIBLE)
502 // For each text chunk, run the regexp
503 QString nodeString = n->nodeValue().string();
504 // add 100 for slop, to make it more likely that we'll search whole nodes
505 if (lengthSearched + nodeString.length() > maxCharsSearched) {
506 nodeString = nodeString.right(charsSearchedThreshold - lengthSearched);
508 int pos = regExp->searchRev(nodeString);
510 return nodeString.mid(pos, regExp->matchedLength()).getNSString();
512 lengthSearched += nodeString.length();
517 // If we started in a cell, but bailed because we found the start of the form or the
518 // previous element, we still might need to search the row above us for a label.
519 if (startingTableCell && !searchedCellAbove) {
520 return searchForLabelsAboveCell(regExp, startingTableCell);
526 NSString *KWQKHTMLPart::matchLabelsAgainstElement(NSArray *labels, ElementImpl *element)
528 QString name = element->getAttribute(ATTR_NAME).string();
529 // Make numbers and _'s in field names behave like word boundaries, e.g., "address2"
530 name.replace(QRegExp("[[:digit:]]"), " ");
531 name.replace('_', ' ');
533 QRegExp *regExp = regExpForLabels(labels);
534 // Use the largest match we can find in the whole name string
541 pos = regExp->search(name, start);
543 length = regExp->matchedLength();
544 if (length >= bestLength) {
553 return name.mid(bestPos, bestLength).getNSString();
559 // Search from the end of the currently selected location if we are first responder, or from
560 // the beginning of the document if nothing is selected or we're not first responder.
561 bool KWQKHTMLPart::findString(NSString *string, bool forward, bool caseFlag, bool wrapFlag)
563 QString target = QString::fromNSString(string);
564 if (target.isEmpty()) {
568 // Start on the correct edge of the selection, search to edge of document.
569 Range searchRange(xmlDocImpl());
570 searchRange.selectNodeContents(xmlDocImpl());
571 if (selectionStart()) {
573 // Must ensure the position is on a container to be usable with a DOMRange
574 Position selEnd = selection().rangeEnd();
575 searchRange.setStart(selEnd.node(), selEnd.offset());
577 // Must ensure the position is on a container to be usable with a DOMRange
578 Position selStart = selection().rangeStart();
579 searchRange.setEnd(selStart.node(), selStart.offset());
583 // Do the search once, then do it a second time to handle wrapped search.
584 // Searches some or all of document twice in the failure case, but that's probably OK.
585 Range resultRange = findPlainText(searchRange, target, forward, caseFlag);
586 if (resultRange.collapsed() && wrapFlag) {
587 searchRange.selectNodeContents(xmlDocImpl());
588 resultRange = findPlainText(searchRange, target, forward, caseFlag);
589 // If we got back to the same place we started, that doesn't count as success.
590 if (resultRange == selection().toRange()) {
595 if (resultRange.collapsed()) {
599 setSelection(resultRange);
604 void KWQKHTMLPart::clearRecordedFormValues()
606 // It's safe to assume that our own classes and Foundation data
607 // structures won't raise exceptions in dealloc
609 KWQRelease(_formValuesAboutToBeSubmitted);
610 _formValuesAboutToBeSubmitted = nil;
611 KWQRelease(_formAboutToBeSubmitted);
612 _formAboutToBeSubmitted = nil;
615 void KWQKHTMLPart::recordFormValue(const QString &name, const QString &value, HTMLFormElementImpl *element)
617 // It's safe to assume that our own classes and basic Foundation
618 // data structures won't raise exceptions
620 if (!_formValuesAboutToBeSubmitted) {
621 _formValuesAboutToBeSubmitted = KWQRetainNSRelease([[NSMutableDictionary alloc] init]);
622 ASSERT(!_formAboutToBeSubmitted);
623 _formAboutToBeSubmitted = KWQRetain([DOMElement _elementWithImpl:element]);
625 ASSERT([_formAboutToBeSubmitted _elementImpl] == element);
627 [_formValuesAboutToBeSubmitted setObject:value.getNSString() forKey:name.getNSString()];
630 void KWQKHTMLPart::submitForm(const KURL &url, const URLArgs &args)
632 KWQ_BLOCK_EXCEPTIONS;
634 // The form multi-submit logic here is only right when we are submitting a form that affects this frame.
635 // Eventually when we find a better fix we can remove this altogether.
636 WebCoreBridge *target = args.frameName.isEmpty() ? _bridge : [_bridge findFrameNamed:args.frameName.getNSString()];
637 KHTMLPart *targetPart = [target part];
638 bool willReplaceThisFrame = false;
639 for (KHTMLPart *p = this; p; p = p->parentPart()) {
640 if (p == targetPart) {
641 willReplaceThisFrame = true;
645 if (willReplaceThisFrame) {
646 // We do not want to submit more than one form from the same page,
647 // nor do we want to submit a single form more than once.
648 // This flag prevents these from happening.
649 // Note that the flag is reset in setView()
650 // since this part may get reused if it is pulled from the b/f cache.
651 if (_submittedFormURL == url) {
654 _submittedFormURL = url;
657 if (!args.doPost()) {
658 [_bridge loadURL:url.getNSURL()
659 referrer:[_bridge referrer]
662 target:args.frameName.getNSString()
663 triggeringEvent:_currentEvent
664 form:_formAboutToBeSubmitted
665 formValues:_formValuesAboutToBeSubmitted];
667 ASSERT(args.contentType().startsWith("Content-Type: "));
668 [_bridge postWithURL:url.getNSURL()
669 referrer:[_bridge referrer]
670 target:args.frameName.getNSString()
671 data:[NSData dataWithBytes:args.postData.data() length:args.postData.size()]
672 contentType:args.contentType().mid(14).getNSString()
673 triggeringEvent:_currentEvent
674 form:_formAboutToBeSubmitted
675 formValues:_formValuesAboutToBeSubmitted];
677 clearRecordedFormValues();
679 KWQ_UNBLOCK_EXCEPTIONS;
682 void KWQKHTMLPart::setEncoding(const QString &name, bool userChosen)
684 if (!d->m_workingURL.isEmpty()) {
687 d->m_encoding = name;
688 d->m_haveEncoding = userChosen;
691 void KWQKHTMLPart::addData(const char *bytes, int length)
693 ASSERT(d->m_workingURL.isEmpty());
695 ASSERT(d->m_doc->parsing());
696 write(bytes, length);
699 void KHTMLPart::frameDetached()
701 KWQ_BLOCK_EXCEPTIONS;
702 [KWQ(this)->bridge() frameDetached];
703 KWQ_UNBLOCK_EXCEPTIONS;
705 // FIXME: There may be a better place to do this that works for KHTML too.
706 KHTMLPart *parent = parentPart();
708 FrameList& parentFrames = parent->d->m_frames;
709 FrameIt end = parentFrames.end();
710 for (FrameIt it = parentFrames.begin(); it != end; ++it) {
711 if ((*it).m_part == this) {
712 parentFrames.remove(it);
720 void KWQKHTMLPart::urlSelected(const KURL &url, int button, int state, const URLArgs &args)
722 KWQ_BLOCK_EXCEPTIONS;
723 [_bridge loadURL:url.getNSURL()
724 referrer:[_bridge referrer]
727 target:args.frameName.getNSString()
728 triggeringEvent:_currentEvent
731 KWQ_UNBLOCK_EXCEPTIONS;
734 class KWQPluginPart : public ReadOnlyPart
736 virtual bool openURL(const KURL &) { return true; }
737 virtual bool closeURL() { return true; }
740 ReadOnlyPart *KWQKHTMLPart::createPart(const ChildFrame &child, const KURL &url, const QString &mimeType)
742 KWQ_BLOCK_EXCEPTIONS;
745 BOOL needFrame = [_bridge frameRequiredForMIMEType:mimeType.getNSString() URL:url.getNSURL()];
746 if (child.m_type == ChildFrame::Object && !needFrame) {
747 NSMutableArray *attributesArray = [NSMutableArray arrayWithCapacity:child.m_params.count()];
748 for (uint i = 0; i < child.m_params.count(); i++) {
749 [attributesArray addObject:child.m_params[i].getNSString()];
752 KWQPluginPart *newPart = new KWQPluginPart;
753 newPart->setWidget(new QWidget([_bridge viewForPluginWithURL:url.getNSURL()
754 attributes:attributesArray
755 baseURL:KURL(d->m_doc->baseURL()).getNSURL()
756 MIMEType:child.m_args.serviceType.getNSString()]));
759 LOG(Frames, "name %s", child.m_name.ascii());
760 BOOL allowsScrolling = YES;
761 int marginWidth = -1;
762 int marginHeight = -1;
763 if (child.m_type != ChildFrame::Object) {
764 HTMLFrameElementImpl *o = static_cast<HTMLFrameElementImpl *>(child.m_frame->element());
765 allowsScrolling = o->scrollingMode() != QScrollView::AlwaysOff;
766 marginWidth = o->getMarginWidth();
767 marginHeight = o->getMarginHeight();
769 WebCoreBridge *childBridge = [_bridge createChildFrameNamed:child.m_name.getNSString()
770 withURL:url.getNSURL()
771 renderPart:child.m_frame
772 allowsScrolling:allowsScrolling
773 marginWidth:marginWidth
774 marginHeight:marginHeight];
775 // This call needs to return an object with a ref, since the caller will expect to own it.
776 // childBridge owns the only ref so far.
777 [childBridge part]->ref();
778 part = [childBridge part];
783 KWQ_UNBLOCK_EXCEPTIONS;
788 void KWQKHTMLPart::setView(KHTMLView *view)
790 // Detach the document now, so any onUnload handlers get run - if
791 // we wait until the view is destroyed, then things won't be
792 // hooked up enough for some JavaScript calls to work.
793 if (d->m_doc && view == NULL) {
806 // Only one form submission is allowed per view of a part.
807 // Since this part may be getting reused as a result of being
808 // pulled from the back/forward cache, reset this flag.
809 _submittedFormURL = KURL();
812 KHTMLView *KWQKHTMLPart::view() const
817 void KWQKHTMLPart::setTitle(const DOMString &title)
819 QString text = title.string();
820 text.replace('\\', backslashAsCurrencySymbol());
822 KWQ_BLOCK_EXCEPTIONS;
823 [_bridge setTitle:text.getNSString()];
824 KWQ_UNBLOCK_EXCEPTIONS;
827 void KWQKHTMLPart::setStatusBarText(const QString &status)
829 QString text = status;
830 text.replace('\\', backslashAsCurrencySymbol());
832 KWQ_BLOCK_EXCEPTIONS;
833 [_bridge setStatusText:text.getNSString()];
834 KWQ_UNBLOCK_EXCEPTIONS;
837 void KWQKHTMLPart::scheduleClose()
839 KWQ_BLOCK_EXCEPTIONS;
840 [_bridge closeWindowSoon];
841 KWQ_UNBLOCK_EXCEPTIONS;
844 void KWQKHTMLPart::unfocusWindow()
846 KWQ_BLOCK_EXCEPTIONS;
847 [_bridge unfocusWindow];
848 KWQ_UNBLOCK_EXCEPTIONS;
851 void KWQKHTMLPart::jumpToSelection()
853 // Assumes that selection will only ever be text nodes. This is currently
854 // true, but will it always be so?
855 if (d->m_selection.start().notEmpty()) {
856 RenderText *rt = dynamic_cast<RenderText *>(d->m_selection.start().node()->renderer());
859 rt->posOfChar(d->m_selection.start().offset(), x, y);
860 // The -50 offset is copied from KHTMLPart::findTextNext, which sets the contents position
861 // after finding a matched text string.
862 d->m_view->setContentsPos(x - 50, y - 50);
865 Something like this would fix <rdar://problem/3154293>: "Find Next should not scroll page if the next target is already visible"
867 I think this would be a better way to do this, to avoid needless horizontal scrolling,
868 but it is not feasible until selectionRect() returns a tighter rect around the
869 selected text. Right now it works at element granularity.
871 NSView *docView = d->m_view->getDocumentView();
873 KWQ_BLOCK_EXCEPTIONS;
874 NSRect selRect = NSRect(selectionRect());
875 NSRect visRect = [docView visibleRect];
876 if (!NSContainsRect(visRect, selRect)) {
877 // pad a bit so we overscroll slightly
878 selRect = NSInsetRect(selRect, -10.0, -10.0);
879 selRect = NSIntersectionRect(selRect, [docView bounds]);
880 [docView scrollRectToVisible:selRect];
882 KWQ_UNBLOCK_EXCEPTIONS;
887 QString KWQKHTMLPart::advanceToNextMisspelling()
889 // The basic approach is to search in two phases - from the selection end to the end of the doc, and
890 // the we wrap and search from the doc start to (approximately) where we started.
892 // Start at the end of the selection, search to edge of document. Starting at the selection end makes
893 // repeated "check spelling" commands work.
894 Range searchRange(xmlDocImpl());
895 searchRange.selectNodeContents(xmlDocImpl());
896 bool startedWithSelection = false;
897 if (selectionStart()) {
898 startedWithSelection = true;
899 // Must ensure the position is on a container to be usable with a DOMRange
900 Position selEnd = selection().rangeEnd();
901 searchRange.setStart(selEnd.node(), selEnd.offset());
904 // If we're not in an editable node, try to find one, make that our range to work in
905 NodeImpl *editableNodeImpl = searchRange.startContainer().handle();
906 if (!editableNodeImpl->isContentEditable()) {
907 editableNodeImpl = editableNodeImpl->nextEditable();
908 if (!editableNodeImpl) {
911 searchRange.setStartBefore(Node(editableNodeImpl));
912 startedWithSelection = false; // won't need to wrap
915 // topNode defines the whole range we want to operate on
916 Node topNode(editableNodeImpl->rootEditableElement());
917 searchRange.setEndAfter(topNode);
919 // Make sure start of searchRange is not in the middle of a word. Jumping back a char and then
920 // forward by a word happens to do the trick.
921 if (startedWithSelection) {
922 Position start(searchRange.startContainer().handle(), searchRange.startOffset());
923 Position newStart = start.previousCharacterPosition();
924 if (newStart != start) {
925 newStart = newStart.nextWordBoundary();
926 // Must ensure the position is on a container to be usable with a DOMRange
927 newStart = newStart.equivalentRangeCompliantPosition();
928 searchRange.setStart(Node(newStart.node()), newStart.offset());
929 } // else we were already at the start of the editable node
932 if (searchRange.collapsed()) {
933 return QString(); // nothing to search in
936 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
937 WordAwareIterator it(searchRange);
938 bool wrapped = false;
940 Node searchEndAfterWrapNode;
941 long searchEndAfterWrapOffset;
943 // it.range() is not valid if we're already at the end
944 searchEndAfterWrapNode = searchRange.startContainer();
945 searchEndAfterWrapOffset = searchRange.startOffset();
947 // We go to the end of our first range insted of the start of it, just to be sure
948 // we don't get foiled by any word boundary problems at the start. It means we might
949 // do a tiny bit more searching.
950 searchEndAfterWrapNode = it.range().endContainer();
951 searchEndAfterWrapOffset = it.range().endOffset();
955 if (!it.atEnd()) { // we may be starting at the end of the doc, and already by atEnd
956 const QChar *chars = it.characters();
957 long len = it.length();
958 if (len > 1 || !chars[0].isSpace()) {
959 NSString *chunk = [[NSString alloc] initWithCharactersNoCopy:(unichar *)chars length:len freeWhenDone:NO];
960 NSRange misspelling = [checker checkSpellingOfString:chunk startingAt:0 language:nil wrap:NO inSpellDocumentWithTag:[_bridge spellCheckerDocumentTag] wordCount:NULL];
962 if (misspelling.length > 0) {
963 // build up result range and string, possibly reiterating over text nodes
964 Range misspellingRange(xmlDocImpl());
965 CharacterIterator chars(it.range());
966 chars.advance(misspelling.location);
967 misspellingRange.setStart(chars.range().startContainer(), chars.range().startOffset());
968 QString result = chars.string(misspelling.length);
970 misspellingRange.setEnd(it.range().endContainer(), it.range().endOffset());
972 misspellingRange.setEnd(chars.range().startContainer(), chars.range().startOffset());
975 setSelection(misspellingRange);
978 // TODO: mark misspelling in document
987 if (wrapped || !startedWithSelection) {
988 break; // finished the second range, or we did the whole doc with the first range
990 // we've gone from the selection to the end of doc, now wrap around
992 searchRange.setStartBefore(topNode);
993 // going until the end of the very first chunk we tested is far enough
994 searchRange.setEnd(searchEndAfterWrapNode, searchEndAfterWrapOffset);
995 it = WordAwareIterator(searchRange);
1003 void KWQKHTMLPart::redirectionTimerStartedOrStopped()
1005 // Don't report history navigations, just actual redirection.
1006 if (d->m_scheduledRedirection == historyNavigationScheduled) {
1010 KWQ_BLOCK_EXCEPTIONS;
1011 if (d->m_redirectionTimer.isActive()) {
1012 [_bridge reportClientRedirectToURL:KURL(d->m_redirectURL).getNSURL()
1013 delay:d->m_delayRedirect
1014 fireDate:[d->m_redirectionTimer.getNSTimer() fireDate]
1015 lockHistory:d->m_redirectLockHistory
1016 isJavaScriptFormAction:d->m_executingJavaScriptFormAction];
1018 [_bridge reportClientRedirectCancelled:d->m_cancelWithLoadInProgress];
1020 KWQ_UNBLOCK_EXCEPTIONS;
1023 void KWQKHTMLPart::paint(QPainter *p, const QRect &rect)
1027 if (p->device()->devType() == QInternal::Printer)
1028 fillWithRed = false; // Printing, don't fill with red (can't remember why).
1029 else if (!xmlDocImpl() || xmlDocImpl()->ownerElement())
1030 fillWithRed = false; // Subframe, don't fill with red.
1031 else if (view() && view()->isTransparent())
1032 fillWithRed = false; // Transparent, don't fill with red.
1033 else if (_drawSelectionOnly)
1034 fillWithRed = false; // Selections are transparent, don't fill with red.
1035 else if (_elementToDraw != 0)
1036 fillWithRed = false; // Element images are transparent, don't fill with red.
1041 p->fillRect(rect.x(), rect.y(), rect.width(), rect.height(), QColor(0xFF, 0, 0));
1046 // _elementToDraw is used to draw only one element
1047 RenderObject *eltRenderer = (_elementToDraw != 0) ? _elementToDraw.handle()->renderer() : 0;
1048 renderer()->layer()->paint(p, rect, _drawSelectionOnly, eltRenderer);
1050 ERROR("called KWQKHTMLPart::paint with nil renderer");
1054 void KWQKHTMLPart::adjustPageHeight(float *newBottom, float oldTop, float oldBottom, float bottomLimit)
1056 RenderCanvas *root = static_cast<RenderCanvas *>(xmlDocImpl()->renderer());
1058 // Use a printer device, with painting disabled for the pagination phase
1059 QPainter painter(true);
1060 painter.setPaintingDisabled(true);
1062 root->setTruncatedAt((int)floor(oldBottom));
1063 QRect dirtyRect(0, (int)floor(oldTop),
1064 root->docWidth(), (int)ceil(oldBottom-oldTop));
1065 root->layer()->paint(&painter, dirtyRect);
1066 *newBottom = root->bestTruncatedAt();
1067 if (*newBottom == 0) {
1068 *newBottom = oldBottom;
1071 *newBottom = oldBottom;
1075 RenderObject *KWQKHTMLPart::renderer()
1077 DocumentImpl *doc = xmlDocImpl();
1078 return doc ? doc->renderer() : 0;
1081 QString KWQKHTMLPart::userAgent() const
1083 KWQ_BLOCK_EXCEPTIONS;
1084 return QString::fromNSString([_bridge userAgentForURL:m_url.getNSURL()]);
1085 KWQ_UNBLOCK_EXCEPTIONS;
1090 QString KWQKHTMLPart::mimeTypeForFileName(const QString &fileName) const
1092 NSString *ns = fileName.getNSString();
1094 KWQ_BLOCK_EXCEPTIONS;
1095 return QString::fromNSString([_bridge MIMETypeForPath:ns]);
1096 KWQ_UNBLOCK_EXCEPTIONS;
1101 NSView *KWQKHTMLPart::nextKeyViewInFrame(NodeImpl *node, KWQSelectionDirection direction)
1103 DocumentImpl *doc = xmlDocImpl();
1108 node = direction == KWQSelectingNext
1109 ? doc->nextFocusNode(node) : doc->previousFocusNode(node);
1113 RenderWidget *renderWidget = dynamic_cast<RenderWidget *>(node->renderer());
1115 QWidget *widget = renderWidget->widget();
1116 KHTMLView *childFrameWidget = dynamic_cast<KHTMLView *>(widget);
1117 if (childFrameWidget) {
1118 NSView *view = KWQ(childFrameWidget->part())->nextKeyViewInFrame(0, direction);
1122 } else if (widget) {
1123 NSView *view = widget->getView();
1124 // AppKit won't be able to handle scrolling and making us the first responder
1125 // well unless we are actually installed in the correct place. KHTML only does
1126 // that for visible widgets, so we need to do it explicitly here.
1128 if (view && renderWidget->absolutePosition(x, y)) {
1129 renderWidget->view()->addChild(widget, x, y);
1135 doc->setFocusNode(node);
1137 view()->ensureRectVisibleCentered(node->getRect());
1139 [_bridge makeFirstResponder:[_bridge documentView]];
1140 return [_bridge documentView];
1145 NSView *KWQKHTMLPart::nextKeyViewInFrameHierarchy(NodeImpl *node, KWQSelectionDirection direction)
1147 NSView *next = nextKeyViewInFrame(node, direction);
1152 // remove focus from currently focused node
1153 DocumentImpl *doc = xmlDocImpl();
1155 doc->setFocusNode(0);
1158 KWQKHTMLPart *parent = KWQ(parentPart());
1160 next = parent->nextKeyView(parent->childFrame(this)->m_frame->element(), direction);
1169 NSView *KWQKHTMLPart::nextKeyView(NodeImpl *node, KWQSelectionDirection direction)
1171 KWQ_BLOCK_EXCEPTIONS;
1173 NSView * next = nextKeyViewInFrameHierarchy(node, direction);
1178 // Look at views from the top level part up, looking for a next key view that we can use.
1180 next = direction == KWQSelectingNext
1181 ? [_bridge nextKeyViewOutsideWebFrameViews]
1182 : [_bridge previousKeyViewOutsideWebFrameViews];
1188 KWQ_UNBLOCK_EXCEPTIONS;
1190 // If all else fails, make a loop by starting from 0.
1191 return nextKeyViewInFrameHierarchy(0, direction);
1194 NSView *KWQKHTMLPart::nextKeyViewForWidget(QWidget *startingWidget, KWQSelectionDirection direction)
1196 // Use the event filter object to figure out which RenderWidget owns this QWidget and get to the DOM.
1197 // Then get the next key view in the order determined by the DOM.
1198 NodeImpl *node = nodeForWidget(startingWidget);
1200 return partForNode(node)->nextKeyView(node, direction);
1203 bool KWQKHTMLPart::currentEventIsMouseDownInWidget(QWidget *candidate)
1205 KWQ_BLOCK_EXCEPTIONS;
1206 switch ([[NSApp currentEvent] type]) {
1207 case NSLeftMouseDown:
1208 case NSRightMouseDown:
1209 case NSOtherMouseDown:
1214 KWQ_UNBLOCK_EXCEPTIONS;
1216 NodeImpl *node = nodeForWidget(candidate);
1218 return partForNode(node)->nodeUnderMouse() == node;
1221 bool KWQKHTMLPart::currentEventIsKeyboardOptionTab()
1223 KWQ_BLOCK_EXCEPTIONS;
1224 NSEvent *evt = [NSApp currentEvent];
1225 if ([evt type] != NSKeyDown) {
1229 if (([evt modifierFlags] & NSAlternateKeyMask) == 0) {
1233 NSString *chars = [evt charactersIgnoringModifiers];
1234 if ([chars length] != 1)
1237 const unichar tabKey = 0x0009;
1238 const unichar shiftTabKey = 0x0019;
1239 unichar c = [chars characterAtIndex:0];
1240 if (c != tabKey && c != shiftTabKey)
1243 KWQ_UNBLOCK_EXCEPTIONS;
1247 bool KWQKHTMLPart::handleKeyboardOptionTabInView(NSView *view)
1249 if (KWQKHTMLPart::currentEventIsKeyboardOptionTab()) {
1250 if (([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) != 0) {
1251 [[view window] selectKeyViewPrecedingView:view];
1253 [[view window] selectKeyViewFollowingView:view];
1261 bool KWQKHTMLPart::tabsToLinks() const
1263 if ([_bridge keyboardUIMode] & WebCoreKeyboardAccessTabsToLinks)
1264 return !KWQKHTMLPart::currentEventIsKeyboardOptionTab();
1266 return KWQKHTMLPart::currentEventIsKeyboardOptionTab();
1269 bool KWQKHTMLPart::tabsToAllControls() const
1271 if ([_bridge keyboardUIMode] & WebCoreKeyboardAccessFull)
1272 return !KWQKHTMLPart::currentEventIsKeyboardOptionTab();
1274 return KWQKHTMLPart::currentEventIsKeyboardOptionTab();
1277 KJS::Bindings::RootObject *KWQKHTMLPart::bindingRootObject()
1279 if (!_bindingRoot) {
1280 _bindingRoot = new KJS::Bindings::RootObject(0); // The root gets deleted by JavaScriptCore.
1281 KJS::ObjectImp *win = static_cast<KJS::ObjectImp *>(KJS::Window::retrieveWindow(this));
1282 _bindingRoot->setRootObjectImp (win);
1283 _bindingRoot->setInterpreter (KJSProxy::proxy(this)->interpreter());
1284 addPluginRootObject (_bindingRoot);
1286 return _bindingRoot;
1289 WebScriptObject *KWQKHTMLPart::windowScriptObject()
1291 if (!_windowScriptObject) {
1292 KJS::ObjectImp *win = static_cast<KJS::ObjectImp *>(KJS::Window::retrieveWindow(this));
1293 _windowScriptObject = KWQRetainNSRelease([[WebScriptObject alloc] _initWithObjectImp:win root:bindingRootObject()]);
1296 return _windowScriptObject;
1299 NPObject *KWQKHTMLPart::windowScriptNPObject()
1301 if (!_windowScriptNPObject) {
1302 KJS::ObjectImp *win = static_cast<KJS::ObjectImp *>(KJS::Window::retrieveWindow(this));
1303 _windowScriptNPObject = _NPN_CreateScriptObject (win, bindingRootObject());
1306 return _windowScriptNPObject;
1309 void KWQKHTMLPart::partClearedInBegin()
1311 [_bridge windowObjectCleared];
1314 QMap<int, ScheduledAction*> *KWQKHTMLPart::pauseActions(const void *key)
1316 if (d->m_doc && d->m_jscript) {
1317 Window *w = Window::retrieveWindow(this);
1318 if (w && w->hasTimeouts()) {
1319 return w->pauseTimeouts(key);
1325 void KWQKHTMLPart::resumeActions(QMap<int, ScheduledAction*> *actions, const void *key)
1327 if (d->m_doc && d->m_jscript && d->m_bJScriptEnabled) {
1328 Window *w = Window::retrieveWindow(this);
1330 w->resumeTimeouts(actions, key);
1335 bool KWQKHTMLPart::canCachePage()
1337 // Only save page state if:
1338 // 1. We're not a frame or frameset.
1339 // 2. The page has no unload handler.
1340 // 3. The page has no password fields.
1341 // 4. The URL for the page is not https.
1342 // 5. The page has no applets.
1343 if (d->m_frames.count() ||
1345 m_url.protocol().startsWith("https") ||
1346 (d->m_doc && (htmlDocument().applets().length() != 0 ||
1347 d->m_doc->hasWindowEventListener(EventImpl::UNLOAD_EVENT) ||
1348 d->m_doc->hasPasswordField()))) {
1354 void KWQKHTMLPart::saveWindowProperties(SavedProperties *windowProperties)
1356 Window *window = Window::retrieveWindow(this);
1358 window->saveProperties(*windowProperties);
1361 void KWQKHTMLPart::saveLocationProperties(SavedProperties *locationProperties)
1363 Window *window = Window::retrieveWindow(this);
1365 Interpreter::lock();
1366 Location *location = window->location();
1367 Interpreter::unlock();
1368 location->saveProperties(*locationProperties);
1372 void KWQKHTMLPart::restoreWindowProperties(SavedProperties *windowProperties)
1374 Window *window = Window::retrieveWindow(this);
1376 window->restoreProperties(*windowProperties);
1379 void KWQKHTMLPart::restoreLocationProperties(SavedProperties *locationProperties)
1381 Window *window = Window::retrieveWindow(this);
1383 Interpreter::lock();
1384 Location *location = window->location();
1385 Interpreter::unlock();
1386 location->restoreProperties(*locationProperties);
1390 void KWQKHTMLPart::saveInterpreterBuiltins(SavedBuiltins &interpreterBuiltins)
1392 if (jScript() && jScript()->interpreter()) {
1393 jScript()->interpreter()->saveBuiltins(interpreterBuiltins);
1397 void KWQKHTMLPart::restoreInterpreterBuiltins(const SavedBuiltins &interpreterBuiltins)
1399 if (jScript() && jScript()->interpreter()) {
1400 jScript()->interpreter()->restoreBuiltins(interpreterBuiltins);
1404 void KWQKHTMLPart::openURLFromPageCache(KWQPageState *state)
1406 // It's safe to assume none of the KWQPageState methods will raise
1407 // exceptions, since KWQPageState is implemented by WebCore and
1410 DocumentImpl *doc = [state document];
1411 KURL *url = [state URL];
1412 SavedProperties *windowProperties = [state windowProperties];
1413 SavedProperties *locationProperties = [state locationProperties];
1414 SavedBuiltins *interpreterBuiltins = [state interpreterBuiltins];
1415 QMap<int, ScheduledAction*> *actions = [state pausedActions];
1417 cancelRedirection();
1419 // We still have to close the previous part page.
1420 if (!d->m_restored){
1424 d->m_bComplete = false;
1426 // Don't re-emit the load event.
1427 d->m_bLoadEventEmitted = true;
1429 // delete old status bar msg's from kjs (if it _was_ activated on last URL)
1430 if( d->m_bJScriptEnabled )
1432 d->m_kjsStatusBarText = QString::null;
1433 d->m_kjsDefaultStatusBarText = QString::null;
1440 // initializing m_url to the new url breaks relative links when opening such a link after this call and _before_ begin() is called (when the first
1441 // data arrives) (Simon)
1442 if(m_url.protocol().startsWith( "http" ) && !m_url.host().isEmpty() && m_url.path().isEmpty()) {
1444 emit d->m_extension->setLocationBarURL( m_url.prettyURL() );
1447 // copy to m_workingURL after fixing m_url above
1448 d->m_workingURL = m_url;
1452 // -----------begin-----------
1455 doc->setInPageCache(NO);
1457 d->m_bCleared = false;
1459 d->m_bComplete = false;
1460 d->m_bLoadEventEmitted = false;
1461 d->m_referrer = m_url.url();
1463 setView(doc->view());
1468 Decoder *decoder = doc->decoder();
1473 d->m_decoder->deref();
1475 d->m_decoder = decoder;
1477 updatePolicyBaseURL();
1479 restoreWindowProperties (windowProperties);
1480 restoreLocationProperties (locationProperties);
1481 restoreInterpreterBuiltins (*interpreterBuiltins);
1484 resumeActions (actions, state);
1489 KWQKHTMLPart *KWQKHTMLPart::partForWidget(const QWidget *widget)
1491 ASSERT_ARG(widget, widget);
1493 NodeImpl *node = nodeForWidget(widget);
1495 return partForNode(node);
1498 // Assume all widgets are either form controls, or KHTMLViews.
1499 const KHTMLView *view = dynamic_cast<const KHTMLView *>(widget);
1501 return KWQ(view->part());
1504 WebCoreBridge *KWQKHTMLPart::bridgeForWidget(const QWidget *widget)
1506 ASSERT_ARG(widget, widget);
1508 KWQKHTMLPart *part = partForWidget(widget);
1510 return part->bridge();
1513 KWQKHTMLPart *KWQKHTMLPart::partForNode(NodeImpl *node)
1515 ASSERT_ARG(node, node);
1516 return KWQ(node->getDocument()->part());
1519 NSView *KWQKHTMLPart::documentViewForNode(DOM::NodeImpl *node)
1521 WebCoreBridge *bridge = partForNode(node)->bridge();
1522 return [bridge documentView];
1525 NodeImpl *KWQKHTMLPart::nodeForWidget(const QWidget *widget)
1527 ASSERT_ARG(widget, widget);
1528 const QObject *o = widget->eventFilterObject();
1529 return o ? static_cast<const RenderWidget *>(o)->element() : 0;
1532 void KWQKHTMLPart::setDocumentFocus(QWidget *widget)
1534 NodeImpl *node = nodeForWidget(widget);
1536 node->getDocument()->setFocusNode(node);
1538 ERROR("unable to clear focus because widget had no corresponding node");
1542 void KWQKHTMLPart::clearDocumentFocus(QWidget *widget)
1544 NodeImpl *node = nodeForWidget(widget);
1546 node->getDocument()->setFocusNode(0);
1548 ERROR("unable to clear focus because widget had no corresponding node");
1552 void KWQKHTMLPart::saveDocumentState()
1554 // Do not save doc state if the page has a password field and a form that would be submitted
1556 if (!(d->m_doc && d->m_doc->hasPasswordField() && d->m_doc->hasSecureForm())) {
1557 KWQ_BLOCK_EXCEPTIONS;
1558 [_bridge saveDocumentState];
1559 KWQ_UNBLOCK_EXCEPTIONS;
1563 void KWQKHTMLPart::restoreDocumentState()
1565 KWQ_BLOCK_EXCEPTIONS;
1566 [_bridge restoreDocumentState];
1567 KWQ_UNBLOCK_EXCEPTIONS;
1570 QPtrList<KWQKHTMLPart> &KWQKHTMLPart::mutableInstances()
1572 static QPtrList<KWQKHTMLPart> instancesList;
1573 return instancesList;
1576 void KWQKHTMLPart::updatePolicyBaseURL()
1578 // FIXME: docImpl() returns null for everything other than HTML documents; is this causing problems? -dwh
1579 if (parentPart() && parentPart()->docImpl()) {
1580 setPolicyBaseURL(parentPart()->docImpl()->policyBaseURL());
1582 setPolicyBaseURL(m_url.url());
1586 void KWQKHTMLPart::setPolicyBaseURL(const DOMString &s)
1588 // FIXME: XML documents will cause this to return null. docImpl() is
1589 // an HTMLdocument only. -dwh
1591 docImpl()->setPolicyBaseURL(s);
1592 ConstFrameIt end = d->m_frames.end();
1593 for (ConstFrameIt it = d->m_frames.begin(); it != end; ++it) {
1594 ReadOnlyPart *subpart = (*it).m_part;
1595 static_cast<KWQKHTMLPart *>(subpart)->setPolicyBaseURL(s);
1599 QString KWQKHTMLPart::requestedURLString() const
1601 KWQ_BLOCK_EXCEPTIONS;
1602 return QString::fromNSString([_bridge requestedURLString]);
1603 KWQ_UNBLOCK_EXCEPTIONS;
1608 QString KWQKHTMLPart::incomingReferrer() const
1610 KWQ_BLOCK_EXCEPTIONS;
1611 return QString::fromNSString([_bridge incomingReferrer]);
1612 KWQ_UNBLOCK_EXCEPTIONS;
1617 void KWQKHTMLPart::forceLayout()
1619 KHTMLView *v = d->m_view;
1622 // We cannot unschedule a pending relayout, since the force can be called with
1623 // a tiny rectangle from a drawRect update. By unscheduling we in effect
1624 // "validate" and stop the necessary full repaint from occurring. Basically any basic
1625 // append/remove DHTML is broken by this call. For now, I have removed the optimization
1626 // until we have a better invalidation stategy. -dwh
1627 //v->unscheduleRelayout();
1631 void KWQKHTMLPart::forceLayoutWithPageWidthRange(float minPageWidth, float maxPageWidth)
1633 // Dumping externalRepresentation(_part->renderer()).ascii() is a good trick to see
1634 // the state of things before and after the layout
1635 RenderCanvas *root = static_cast<RenderCanvas *>(xmlDocImpl()->renderer());
1637 // This magic is basically copied from khtmlview::print
1638 int pageW = (int)ceil(minPageWidth);
1639 root->setWidth(pageW);
1640 root->setNeedsLayoutAndMinMaxRecalc();
1643 // If we don't fit in the minimum page width, we'll lay out again. If we don't fit in the
1644 // maximum page width, we will lay out to the maximum page width and clip extra content.
1645 // FIXME: We are assuming a shrink-to-fit printing implementation. A cropping
1646 // implementation should not do this!
1647 int rightmostPos = root->rightmostPosition();
1648 if (rightmostPos > minPageWidth) {
1649 pageW = kMin(rightmostPos, (int)ceil(maxPageWidth));
1650 root->setWidth(pageW);
1651 root->setNeedsLayoutAndMinMaxRecalc();
1657 void KWQKHTMLPart::sendResizeEvent()
1659 KHTMLView *v = d->m_view;
1661 // Sending an event can result in the destruction of the view and part.
1662 // We ref so that happens after we return from the KHTMLView function.
1670 void KWQKHTMLPart::sendScrollEvent()
1672 KHTMLView *v = d->m_view;
1674 DocumentImpl *doc = xmlDocImpl();
1677 doc->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, true, false);
1681 void KWQKHTMLPart::runJavaScriptAlert(const QString &message)
1683 QString text = message;
1684 text.replace('\\', backslashAsCurrencySymbol());
1685 KWQ_BLOCK_EXCEPTIONS;
1686 [_bridge runJavaScriptAlertPanelWithMessage:text.getNSString()];
1687 KWQ_UNBLOCK_EXCEPTIONS;
1690 bool KWQKHTMLPart::runJavaScriptConfirm(const QString &message)
1692 QString text = message;
1693 text.replace('\\', backslashAsCurrencySymbol());
1695 KWQ_BLOCK_EXCEPTIONS;
1696 return [_bridge runJavaScriptConfirmPanelWithMessage:text.getNSString()];
1697 KWQ_UNBLOCK_EXCEPTIONS;
1702 bool KWQKHTMLPart::runJavaScriptPrompt(const QString &prompt, const QString &defaultValue, QString &result)
1704 QString promptText = prompt;
1705 promptText.replace('\\', backslashAsCurrencySymbol());
1706 QString defaultValueText = defaultValue;
1707 defaultValueText.replace('\\', backslashAsCurrencySymbol());
1709 KWQ_BLOCK_EXCEPTIONS;
1710 NSString *returnedText = nil;
1712 bool ok = [_bridge runJavaScriptTextInputPanelWithPrompt:prompt.getNSString()
1713 defaultText:defaultValue.getNSString() returningText:&returnedText];
1716 result = QString::fromNSString(returnedText);
1717 result.replace(backslashAsCurrencySymbol(), '\\');
1721 KWQ_UNBLOCK_EXCEPTIONS;
1726 void KWQKHTMLPart::addMessageToConsole(const QString &message, unsigned lineNumber, const QString &sourceURL)
1728 NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
1729 message.getNSString(), @"message",
1730 [NSNumber numberWithInt: lineNumber], @"lineNumber",
1731 sourceURL.getNSString(), @"sourceURL",
1733 [_bridge addMessageToConsole:dictionary];
1736 void KWQKHTMLPart::createEmptyDocument()
1738 // Although it's not completely clear from the name of this function,
1739 // it does nothing if we already have a document, and just creates an
1740 // empty one if we have no document at all.
1742 KWQ_BLOCK_EXCEPTIONS;
1743 [_bridge loadEmptyDocumentSynchronously];
1744 KWQ_UNBLOCK_EXCEPTIONS;
1746 if (parentPart() && (parentPart()->childFrame(this)->m_type == ChildFrame::IFrame ||
1747 parentPart()->childFrame(this)->m_type == ChildFrame::Object)) {
1748 d->m_doc->setBaseURL(parentPart()->d->m_doc->baseURL());
1753 void KWQKHTMLPart::addMetaData(const QString &key, const QString &value)
1755 d->m_job->addMetaData(key, value);
1758 bool KWQKHTMLPart::keyEvent(NSEvent *event)
1760 KWQ_BLOCK_EXCEPTIONS;
1762 ASSERT([event type] == NSKeyDown || [event type] == NSKeyUp);
1764 // Check for cases where we are too early for events -- possible unmatched key up
1765 // from pressing return in the location bar.
1766 DocumentImpl *doc = xmlDocImpl();
1770 NodeImpl *node = doc->focusNode();
1771 if (!node && docImpl()) {
1772 node = docImpl()->body();
1778 NSEvent *oldCurrentEvent = _currentEvent;
1779 _currentEvent = KWQRetain(event);
1781 QKeyEvent qEvent(event);
1782 bool result = !node->dispatchKeyEvent(&qEvent);
1784 // We want to send both a down and a press for the initial key event.
1785 // To get KHTML to do this, we send a second KeyPress QKeyEvent with "is repeat" set to true,
1786 // which causes it to send a press to the DOM.
1787 // That's not a great hack; it would be good to do this in a better way.
1788 if ([event type] == NSKeyDown && ![event isARepeat]) {
1789 QKeyEvent repeatEvent(event, true);
1790 if (!node->dispatchKeyEvent(&repeatEvent)) {
1795 ASSERT(_currentEvent == event);
1797 _currentEvent = oldCurrentEvent;
1801 KWQ_UNBLOCK_EXCEPTIONS;
1806 // This does the same kind of work that KHTMLPart::openURL does, except it relies on the fact
1807 // that a higher level already checked that the URLs match and the scrolling is the right thing to do.
1808 void KWQKHTMLPart::scrollToAnchor(const KURL &URL)
1810 cancelRedirection();
1817 // It's important to model this as a load that starts and immediately finishes.
1818 // Otherwise, the parent frame may think we never finished loading.
1819 d->m_bComplete = false;
1823 bool KWQKHTMLPart::closeURL()
1825 saveDocumentState();
1826 return KHTMLPart::closeURL();
1829 void KWQKHTMLPart::khtmlMousePressEvent(MousePressEvent *event)
1831 bool singleClick = [_currentEvent clickCount] <= 1;
1833 // If we got the event back, that must mean it wasn't prevented,
1834 // so it's allowed to start a drag or selection.
1835 _mouseDownMayStartSelect = true;
1836 // Careful that the drag starting logic stays in sync with eventMayStartDrag()
1837 _mouseDownMayStartDrag = singleClick;
1839 if (!passWidgetMouseDownEventToWidget(event)) {
1840 // We don't do this at the start of mouse down handling (before calling into WebCore),
1841 // because we don't want to do it until we know we didn't hit a widget.
1842 NSView *view = d->m_view->getDocumentView();
1845 KWQ_BLOCK_EXCEPTIONS;
1846 if ([_bridge firstResponder] != view) {
1847 [_bridge makeFirstResponder:view];
1849 KWQ_UNBLOCK_EXCEPTIONS;
1852 KHTMLPart::khtmlMousePressEvent(event);
1856 void KWQKHTMLPart::khtmlMouseDoubleClickEvent(MouseDoubleClickEvent *event)
1858 if (!passWidgetMouseDownEventToWidget(event)) {
1859 KHTMLPart::khtmlMouseDoubleClickEvent(event);
1863 bool KWQKHTMLPart::passWidgetMouseDownEventToWidget(khtml::MouseEvent *event)
1865 // Figure out which view to send the event to.
1866 RenderObject *target = event->innerNode().handle() ? event->innerNode().handle()->renderer() : 0;
1870 QWidget* widget = RenderLayer::gScrollBar;
1872 if (!target->isWidget())
1874 widget = static_cast<RenderWidget *>(target)->widget();
1877 // Doubleclick events don't exist in Cocoa. Since passWidgetMouseDownEventToWidget will
1878 // just pass _currentEvent down to the widget, we don't want to call it for events that
1879 // don't correspond to Cocoa events. The mousedown/ups will have already been passed on as
1880 // part of the pressed/released handling.
1881 if (!MouseDoubleClickEvent::test(event))
1882 return passWidgetMouseDownEventToWidget(widget);
1887 bool KWQKHTMLPart::passWidgetMouseDownEventToWidget(RenderWidget *renderWidget)
1889 return passWidgetMouseDownEventToWidget(renderWidget->widget());
1892 bool KWQKHTMLPart::passWidgetMouseDownEventToWidget(QWidget* widget)
1894 // FIXME: this method always returns true
1897 ERROR("hit a RenderWidget without a corresponding QWidget, means a frame is half-constructed");
1901 KWQ_BLOCK_EXCEPTIONS;
1903 NSView *nodeView = widget->getView();
1905 ASSERT([nodeView superview]);
1906 NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[_currentEvent locationInWindow] fromView:nil]];
1908 ERROR("KHTML says we hit a RenderWidget, but AppKit doesn't agree we hit the corresponding NSView");
1912 if ([_bridge firstResponder] == view) {
1913 // In the case where we just became first responder, we should send the mouseDown:
1914 // to the NSTextField, not the NSTextField's editor. This code makes sure that happens.
1915 // If we don't do this, we see a flash of selected text when clicking in a text field.
1916 if (_firstResponderAtMouseDownTime != view && [view isKindOfClass:[NSTextView class]]) {
1917 NSView *superview = view;
1918 while (superview != nodeView) {
1919 superview = [superview superview];
1921 if ([superview isKindOfClass:[NSControl class]]) {
1922 NSControl *control = superview;
1923 if ([control currentEditor] == view) {
1931 // Normally [NSWindow sendEvent:] handles setting the first responder.
1932 // But in our case, the event was sent to the view representing the entire web page.
1933 if ([_currentEvent clickCount] <= 1 && [view acceptsFirstResponder] && [view needsPanelToBecomeKey]) {
1934 [_bridge makeFirstResponder:view];
1938 // We need to "defer loading" and defer timers while we are tracking the mouse.
1939 // That's because we don't want the new page to load while the user is holding the mouse down.
1941 BOOL wasDeferringLoading = [_bridge defersLoading];
1942 if (!wasDeferringLoading) {
1943 [_bridge setDefersLoading:YES];
1945 BOOL wasDeferringTimers = QObject::defersTimers();
1946 if (!wasDeferringTimers) {
1947 QObject::setDefersTimers(true);
1950 ASSERT(!_sendingEventToSubview);
1951 _sendingEventToSubview = true;
1952 [view mouseDown:_currentEvent];
1953 _sendingEventToSubview = false;
1955 if (!wasDeferringTimers) {
1956 QObject::setDefersTimers(false);
1958 if (!wasDeferringLoading) {
1959 [_bridge setDefersLoading:NO];
1962 // Remember which view we sent the event to, so we can direct the release event properly.
1963 _mouseDownView = view;
1964 _mouseDownWasInSubframe = false;
1966 KWQ_UNBLOCK_EXCEPTIONS;
1971 bool KWQKHTMLPart::lastEventIsMouseUp()
1973 // Many AK widgets run their own event loops and consume events while the mouse is down.
1974 // When they finish, currentEvent is the mouseUp that they exited on. We need to update
1975 // the khtml state with this mouseUp, which khtml never saw. This method lets us detect
1978 KWQ_BLOCK_EXCEPTIONS;
1979 NSEvent *currentEventAfterHandlingMouseDown = [NSApp currentEvent];
1980 if (_currentEvent != currentEventAfterHandlingMouseDown) {
1981 if ([currentEventAfterHandlingMouseDown type] == NSLeftMouseUp) {
1985 KWQ_UNBLOCK_EXCEPTIONS;
1990 // Note that this does the same kind of check as [target isDescendantOf:superview].
1991 // There are two differences: This is a lot slower because it has to walk the whole
1992 // tree, and this works in cases where the target has already been deallocated.
1993 static bool findViewInSubviews(NSView *superview, NSView *target)
1995 KWQ_BLOCK_EXCEPTIONS;
1996 NSEnumerator *e = [[superview subviews] objectEnumerator];
1998 while ((subview = [e nextObject])) {
1999 if (subview == target || findViewInSubviews(subview, target)) {
2003 KWQ_UNBLOCK_EXCEPTIONS;
2008 NSView *KWQKHTMLPart::mouseDownViewIfStillGood()
2010 // Since we have no way of tracking the lifetime of _mouseDownView, we have to assume that
2011 // it could be deallocated already. We search for it in our subview tree; if we don't find
2012 // it, we set it to nil.
2013 NSView *mouseDownView = _mouseDownView;
2014 if (!mouseDownView) {
2017 KHTMLView *topKHTMLView = d->m_view;
2018 NSView *topView = topKHTMLView ? topKHTMLView->getView() : nil;
2019 if (!topView || !findViewInSubviews(topView, mouseDownView)) {
2020 _mouseDownView = nil;
2023 return mouseDownView;
2026 // The link drag hysteresis is much larger than the others because there
2027 // needs to be enough space to cancel the link press without starting a link drag,
2028 // and because dragging links is rare.
2029 #define LinkDragHysteresis 40.0
2030 #define ImageDragHysteresis 5.0
2031 #define TextDragHysteresis 3.0
2032 #define GeneralDragHysteresis 3.0
2034 #define TextDragDelay 0.15
2036 bool KWQKHTMLPart::dragHysteresisExceeded(float dragLocationX, float dragLocationY) const
2039 d->m_view->viewportToContents((int)dragLocationX, (int)dragLocationY, dragX, dragY);
2040 float deltaX = QABS(dragX - _mouseDownX);
2041 float deltaY = QABS(dragY - _mouseDownY);
2043 float threshold = GeneralDragHysteresis;
2044 if (_dragSrcIsImage) {
2045 threshold = ImageDragHysteresis;
2046 } else if (_dragSrcIsLink) {
2047 threshold = LinkDragHysteresis;
2048 } else if (_dragSrcInSelection) {
2049 threshold = TextDragHysteresis;
2051 return deltaX >= threshold || deltaY >= threshold;
2054 // returns if we should continue "default processing", i.e., whether eventhandler canceled
2055 bool KWQKHTMLPart::dispatchDragSrcEvent(int eventId, const QPoint &loc) const
2057 bool noDefaultProc = d->m_view->dispatchDragEvent(eventId, _dragSrc.handle(), loc, _dragClipboard);
2058 return !noDefaultProc;
2061 bool KWQKHTMLPart::eventMayStartDrag(NSEvent *event)
2063 // This is a pre-flight check of whether the event might lead to a drag being started. Be careful
2064 // that its logic needs to stay in sync with khtmlMouseMoveEvent() and the way we set
2065 // _mouseDownMayStartDrag in khtmlMousePressEvent
2067 if ([event type] != NSLeftMouseDown || [event clickCount] != 1) {
2071 BOOL DHTMLFlag, UAFlag;
2072 [_bridge allowDHTMLDrag:&DHTMLFlag UADrag:&UAFlag];
2073 if (!DHTMLFlag && !UAFlag) {
2077 NSPoint loc = [event locationInWindow];
2078 int mouseDownX, mouseDownY;
2079 d->m_view->viewportToContents((int)loc.x, (int)loc.y, mouseDownX, mouseDownY);
2080 RenderObject::NodeInfo nodeInfo(true, false);
2081 renderer()->layer()->nodeAtPoint(nodeInfo, mouseDownX, mouseDownY);
2083 Node possibleSrc = nodeInfo.innerNode()->renderer()->draggableNode(DHTMLFlag, UAFlag, mouseDownX, mouseDownY, srcIsDHTML);
2084 return !possibleSrc.isNull();
2087 void KWQKHTMLPart::khtmlMouseMoveEvent(MouseMoveEvent *event)
2089 KWQ_BLOCK_EXCEPTIONS;
2091 if ([_currentEvent type] == NSLeftMouseDragged) {
2092 NSView *view = mouseDownViewIfStillGood();
2095 _sendingEventToSubview = true;
2096 [view mouseDragged:_currentEvent];
2097 _sendingEventToSubview = false;
2101 // Careful that the drag starting logic stays in sync with eventMayStartDrag()
2103 if (_mouseDownMayStartDrag && _dragSrc.isNull()) {
2104 BOOL tempFlag1, tempFlag2;
2105 [_bridge allowDHTMLDrag:&tempFlag1 UADrag:&tempFlag2];
2106 _dragSrcMayBeDHTML = tempFlag1;
2107 _dragSrcMayBeUA = tempFlag2;
2108 if (!_dragSrcMayBeDHTML && !_dragSrcMayBeUA) {
2109 _mouseDownMayStartDrag = false; // no element is draggable
2113 if (_mouseDownMayStartDrag && _dragSrc.isNull()) {
2114 // try to find an element that wants to be dragged
2115 RenderObject::NodeInfo nodeInfo(true, false);
2116 renderer()->layer()->nodeAtPoint(nodeInfo, _mouseDownX, _mouseDownY);
2117 _dragSrc = nodeInfo.innerNode()->renderer()->draggableNode(_dragSrcMayBeDHTML, _dragSrcMayBeUA, _mouseDownX, _mouseDownY, _dragSrcIsDHTML);
2118 if (_dragSrc.isNull()) {
2119 _mouseDownMayStartDrag = false; // no element is draggable
2121 // remember some facts about this source, while we have a NodeInfo handy
2122 NodeImpl *node = nodeInfo.URLElement();
2123 _dragSrcIsLink = node ? node->hasAnchor() : false;
2125 node = nodeInfo.innerNonSharedNode();
2126 _dragSrcIsImage = (node && node->renderer() && node->renderer()->isImage());
2128 _dragSrcInSelection = isPointInsideSelection(_mouseDownX, _mouseDownY);
2132 // For drags starting in the selection, the user must wait between the mousedown and mousedrag,
2133 // or else we bail on the dragging stuff and allow selection to occur
2134 if (_mouseDownMayStartDrag && _dragSrcInSelection && [_currentEvent timestamp] - _mouseDownTimestamp < TextDragDelay) {
2135 _mouseDownMayStartDrag = false;
2136 // ...but if this was the first click in the window, we don't even want to start selection
2137 if (_activationEventNumber == [_currentEvent eventNumber]) {
2138 _mouseDownMayStartSelect = false;
2142 if (_mouseDownMayStartDrag) {
2143 // We are starting a text/image/url drag, so the cursor should be an arrow
2144 d->m_view->resetCursor();
2146 NSPoint dragLocation = [_currentEvent locationInWindow];
2147 if (dragHysteresisExceeded(dragLocation.x, dragLocation.y)) {
2149 // Once we're past the hysteresis point, we don't want to treat this gesture as a click
2150 d->m_view->invalidateClick();
2152 NSImage *dragImage = nil; // we use these values if WC is out of the loop
2153 NSPoint dragLoc = NSZeroPoint;
2154 NSDragOperation srcOp = NSDragOperationNone;
2155 BOOL wcWrotePasteboard = NO;
2156 if (_dragSrcMayBeDHTML) {
2157 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
2158 // Must be done before ondragstart adds types and data to the pboard,
2159 // also done for security, as it erases data from the last drag
2160 [pasteboard declareTypes:[NSArray array] owner:nil];
2162 freeClipboard(); // would only happen if we missed a dragEnd. Do it anyway, just
2163 // to make sure it gets numbified
2164 _dragClipboard = new KWQClipboard(true, pasteboard, KWQClipboard::Writable, this);
2165 _dragClipboard->ref();
2167 // If this is drag of an element, get set up to generate a default image. Otherwise
2168 // WebKit will generate the default, the element doesn't override.
2169 if (_dragSrcIsDHTML) {
2171 _dragSrc.handle()->renderer()->absolutePosition(srcX, srcY);
2172 _dragClipboard->setDragImageElement(_dragSrc, QPoint(_mouseDownX - srcX, _mouseDownY - srcY));
2175 _mouseDownMayStartDrag = dispatchDragSrcEvent(EventImpl::DRAGSTART_EVENT, QPoint(_mouseDownWinX, _mouseDownWinY));
2176 // Invalidate clipboard here against anymore pasteboard writing for security. The drag
2177 // image can still be changed as we drag, but not the pasteboard data.
2178 _dragClipboard->setAccessPolicy(KWQClipboard::ImageWritable);
2180 if (_mouseDownMayStartDrag) {
2181 // gather values from DHTML element, if it set any
2182 _dragClipboard->sourceOperation(&srcOp);
2184 NSArray *types = [pasteboard types];
2185 wcWrotePasteboard = types && [types count] > 0;
2187 if (_dragSrcMayBeDHTML) {
2188 dragImage = _dragClipboard->dragNSImage(&dragLoc);
2191 // Yuck, dragSourceMovedTo() can be called as a result of kicking off the drag with
2192 // dragImage! Because of that dumb reentrancy, we may think we've not started the
2193 // drag when that happens. So we have to assume it's started before we kick it off.
2194 _dragClipboard->setDragHasStarted();
2198 if (_mouseDownMayStartDrag) {
2199 BOOL startedDrag = [_bridge startDraggingImage:dragImage at:dragLoc operation:srcOp event:_currentEvent sourceIsDHTML:_dragSrcIsDHTML DHTMLWroteData:wcWrotePasteboard];
2200 if (!startedDrag && _dragSrcMayBeDHTML) {
2201 // WebKit canned the drag at the last minute - we owe _dragSrc a DRAGEND event
2202 dispatchDragSrcEvent(EventImpl::DRAGEND_EVENT, QPoint(dragLocation));
2203 _mouseDownMayStartDrag = false;
2207 if (!_mouseDownMayStartDrag) {
2208 // something failed to start the drag, cleanup
2214 // No more default handling (like selection), whether we're past the hysteresis bounds or not
2217 if (!_mouseDownMayStartSelect) {
2221 // Don't allow dragging or click handling after we've started selecting.
2222 _mouseDownMayStartDrag = false;
2223 d->m_view->invalidateClick();
2225 // We use khtml's selection but our own autoscrolling.
2226 [_bridge handleAutoscrollForMouseDragged:_currentEvent];
2228 // If we allowed the other side of the bridge to handle a drag
2229 // last time, then m_bMousePressed might still be set. So we
2230 // clear it now to make sure the next move after a drag
2231 // doesn't look like a drag.
2232 d->m_bMousePressed = false;
2235 KHTMLPart::khtmlMouseMoveEvent(event);
2237 KWQ_UNBLOCK_EXCEPTIONS;
2240 void KWQKHTMLPart::dragSourceMovedTo(const QPoint &loc)
2242 if (!_dragSrc.isNull() && _dragSrcMayBeDHTML) {
2243 // for now we don't care if event handler cancels default behavior, since there is none
2244 dispatchDragSrcEvent(EventImpl::DRAG_EVENT, loc);
2248 void KWQKHTMLPart::dragSourceEndedAt(const QPoint &loc, NSDragOperation operation)
2250 if (!_dragSrc.isNull() && _dragSrcMayBeDHTML) {
2251 _dragClipboard->setDestinationOperation(operation);
2252 // for now we don't care if event handler cancels default behavior, since there is none
2253 dispatchDragSrcEvent(EventImpl::DRAGEND_EVENT, loc);
2259 // Returns whether caller should continue with "the default processing", which is the same as
2260 // the event handler NOT setting the return value to false
2261 bool KWQKHTMLPart::dispatchCPPEvent(int eventId, KWQClipboard::AccessPolicy policy)
2263 NodeImpl *target = d->m_selection.start().element();
2264 if (!target && docImpl()) {
2265 target = docImpl()->body();
2271 KWQClipboard *clipboard = new KWQClipboard(false, [NSPasteboard generalPasteboard], (KWQClipboard::AccessPolicy)policy);
2274 int exceptioncode = 0;
2275 EventImpl *evt = new ClipboardEventImpl(static_cast<EventImpl::EventId>(eventId), true, true, clipboard);
2277 target->dispatchEvent(evt, exceptioncode, true);
2278 bool noDefaultProcessing = evt->defaultPrevented();
2281 // invalidate clipboard here for security
2282 clipboard->setAccessPolicy(KWQClipboard::Numb);
2285 return !noDefaultProcessing;
2288 // WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items. They
2289 // also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items.
2290 // We need to use onbeforecopy as a real menu enabler because we allow elements that are not
2291 // normally selectable to implement copy/paste (like divs, or a document body).
2293 bool KWQKHTMLPart::mayCut()
2295 return !dispatchCPPEvent(EventImpl::BEFORECUT_EVENT, KWQClipboard::Numb);
2298 bool KWQKHTMLPart::mayCopy()
2300 return !dispatchCPPEvent(EventImpl::BEFORECOPY_EVENT, KWQClipboard::Numb);
2303 bool KWQKHTMLPart::mayPaste()
2305 return !dispatchCPPEvent(EventImpl::BEFOREPASTE_EVENT, KWQClipboard::Numb);
2308 bool KWQKHTMLPart::tryCut()
2310 // Must be done before oncut adds types and data to the pboard,
2311 // also done for security, as it erases data from the last copy/paste.
2312 [[NSPasteboard generalPasteboard] declareTypes:[NSArray array] owner:nil];
2314 return !dispatchCPPEvent(EventImpl::CUT_EVENT, KWQClipboard::Writable);
2317 bool KWQKHTMLPart::tryCopy()
2319 // Must be done before oncopy adds types and data to the pboard,
2320 // also done for security, as it erases data from the last copy/paste.
2321 [[NSPasteboard generalPasteboard] declareTypes:[NSArray array] owner:nil];
2323 return !dispatchCPPEvent(EventImpl::COPY_EVENT, KWQClipboard::Writable);
2326 bool KWQKHTMLPart::tryPaste()
2328 return !dispatchCPPEvent(EventImpl::PASTE_EVENT, KWQClipboard::Readable);
2331 void KWQKHTMLPart::khtmlMouseReleaseEvent(MouseReleaseEvent *event)
2333 NSView *view = mouseDownViewIfStillGood();
2335 // If this was the first click in the window, we don't even want to clear the selection.
2336 // This case occurs when the user clicks on a draggable element, since we have to process
2337 // the mouse down and drag events to see if we might start a drag. For other first clicks
2338 // in a window, we just don't acceptFirstMouse, and the whole down-drag-up sequence gets
2339 // ignored upstream of this layer.
2340 if (_activationEventNumber != [_currentEvent eventNumber]) {
2341 KHTMLPart::khtmlMouseReleaseEvent(event);
2346 _sendingEventToSubview = true;
2347 KWQ_BLOCK_EXCEPTIONS;
2348 [view mouseUp:_currentEvent];
2349 KWQ_UNBLOCK_EXCEPTIONS;
2350 _sendingEventToSubview = false;
2353 void KWQKHTMLPart::clearTimers(KHTMLView *view)
2356 view->unscheduleRelayout();
2358 DocumentImpl* document = view->part()->xmlDocImpl();
2359 if (document && document->renderer() && document->renderer()->layer())
2360 document->renderer()->layer()->suspendMarquees();
2365 void KWQKHTMLPart::clearTimers()
2367 clearTimers(d->m_view);
2370 bool KWQKHTMLPart::passSubframeEventToSubframe(NodeImpl::MouseEvent &event)
2372 KWQ_BLOCK_EXCEPTIONS;
2374 switch ([_currentEvent type]) {
2375 case NSLeftMouseDown: {
2376 NodeImpl *node = event.innerNode.handle();
2380 RenderPart *renderPart = dynamic_cast<RenderPart *>(node->renderer());
2384 if (!passWidgetMouseDownEventToWidget(renderPart)) {
2387 _mouseDownWasInSubframe = true;
2390 case NSLeftMouseUp: {
2391 if (!_mouseDownWasInSubframe) {
2394 NSView *view = mouseDownViewIfStillGood();
2398 ASSERT(!_sendingEventToSubview);
2399 _sendingEventToSubview = true;
2400 [view mouseUp:_currentEvent];
2401 _sendingEventToSubview = false;
2404 case NSLeftMouseDragged: {
2405 if (!_mouseDownWasInSubframe) {
2408 NSView *view = mouseDownViewIfStillGood();
2412 ASSERT(!_sendingEventToSubview);
2413 _sendingEventToSubview = true;
2414 [view mouseDragged:_currentEvent];
2415 _sendingEventToSubview = false;
2421 KWQ_UNBLOCK_EXCEPTIONS;
2426 void KWQKHTMLPart::mouseDown(NSEvent *event)
2428 KHTMLView *v = d->m_view;
2429 if (!v || _sendingEventToSubview) {
2433 KWQ_BLOCK_EXCEPTIONS;
2435 _mouseDownView = nil;
2438 NSEvent *oldCurrentEvent = _currentEvent;
2439 _currentEvent = KWQRetain(event);
2440 NSPoint loc = [event locationInWindow];
2441 _mouseDownWinX = (int)loc.x;
2442 _mouseDownWinY = (int)loc.y;
2443 d->m_view->viewportToContents(_mouseDownWinX, _mouseDownWinY, _mouseDownX, _mouseDownY);
2444 _mouseDownTimestamp = [event timestamp];
2446 NSResponder *oldFirstResponderAtMouseDownTime = _firstResponderAtMouseDownTime;
2447 // Unlike other places in WebCore where we get the first
2448 // responder, in this case we must be talking about the real first
2449 // responder, so we could just ask the bridge's window, instead of
2450 // the bridge. It's unclear which is better.
2451 _firstResponderAtMouseDownTime = KWQRetain([_bridge firstResponder]);
2453 _mouseDownMayStartDrag = false;
2454 _mouseDownMayStartSelect = false;
2456 // Sending an event can result in the destruction of the view and part.
2457 // We ref so that happens after we return from the KHTMLView function.
2459 QMouseEvent kEvent(QEvent::MouseButtonPress, event);
2460 v->viewportMousePressEvent(&kEvent);
2463 KWQRelease(_firstResponderAtMouseDownTime);
2464 _firstResponderAtMouseDownTime = oldFirstResponderAtMouseDownTime;
2466 ASSERT(_currentEvent == event);
2468 _currentEvent = oldCurrentEvent;
2470 KWQ_UNBLOCK_EXCEPTIONS;
2473 void KWQKHTMLPart::mouseDragged(NSEvent *event)
2475 KHTMLView *v = d->m_view;
2476 if (!v || _sendingEventToSubview) {
2480 KWQ_BLOCK_EXCEPTIONS;
2482 NSEvent *oldCurrentEvent = _currentEvent;
2483 _currentEvent = KWQRetain(event);
2485 // Sending an event can result in the destruction of the view and part.
2486 // We ref so that happens after we return from the KHTMLView function.
2488 QMouseEvent kEvent(QEvent::MouseMove, event);
2489 v->viewportMouseMoveEvent(&kEvent);
2492 ASSERT(_currentEvent == event);
2494 _currentEvent = oldCurrentEvent;
2496 KWQ_UNBLOCK_EXCEPTIONS;
2499 void KWQKHTMLPart::mouseUp(NSEvent *event)
2501 KHTMLView *v = d->m_view;
2502 if (!v || _sendingEventToSubview) {
2506 KWQ_BLOCK_EXCEPTIONS;
2508 NSEvent *oldCurrentEvent = _currentEvent;
2509 _currentEvent = KWQRetain(event);
2511 // Sending an event can result in the destruction of the view and part.
2512 // We ref so that happens after we return from the KHTMLView function.
2514 // Our behavior here is a little different that Qt. Qt always sends
2515 // a mouse release event, even for a double click. To correct problems
2516 // in khtml's DOM click event handling we do not send a release here
2517 // for a double click. Instead we send that event from KHTMLView's
2518 // viewportMouseDoubleClickEvent. Note also that the third click of
2519 // a triple click is treated as a single click, but the fourth is then
2520 // treated as another double click. Hence the "% 2" below.
2521 int clickCount = [event clickCount];
2522 if (clickCount > 0 && clickCount % 2 == 0) {
2523 QMouseEvent doubleClickEvent(QEvent::MouseButtonDblClick, event);
2524 v->viewportMouseDoubleClickEvent(&doubleClickEvent);
2526 QMouseEvent releaseEvent(QEvent::MouseButtonRelease, event);
2527 v->viewportMouseReleaseEvent(&releaseEvent);
2531 ASSERT(_currentEvent == event);
2533 _currentEvent = oldCurrentEvent;
2535 _mouseDownView = nil;
2537 KWQ_UNBLOCK_EXCEPTIONS;
2541 A hack for the benefit of AK's PopUpButton, which uses the Carbon menu manager, which thus
2542 eats all subsequent events after it is starts its modal tracking loop. After the interaction
2543 is done, this routine is used to fix things up. When a mouse down started us tracking in
2544 the widget, we post a fake mouse up to balance the mouse down we started with. When a
2545 key down started us tracking in the widget, we post a fake key up to balance things out.
2546 In addition, we post a fake mouseMoved to get the cursor in sync with whatever we happen to
2547 be over after the tracking is done.
2549 void KWQKHTMLPart::sendFakeEventsAfterWidgetTracking(NSEvent *initiatingEvent)
2551 KWQ_BLOCK_EXCEPTIONS;
2553 _sendingEventToSubview = false;
2554 int eventType = [initiatingEvent type];
2555 ASSERT(eventType == NSLeftMouseDown || eventType == NSKeyDown);
2556 NSEvent *fakeEvent = nil;
2557 if (eventType == NSLeftMouseDown) {
2558 fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
2559 location:[initiatingEvent locationInWindow]
2560 modifierFlags:[initiatingEvent modifierFlags]
2561 timestamp:[initiatingEvent timestamp]
2562 windowNumber:[initiatingEvent windowNumber]
2563 context:[initiatingEvent context]
2564 eventNumber:[initiatingEvent eventNumber]
2565 clickCount:[initiatingEvent clickCount]
2566 pressure:[initiatingEvent pressure]];
2570 else { // eventType == NSKeyDown
2571 fakeEvent = [NSEvent keyEventWithType:NSKeyUp
2572 location:[initiatingEvent locationInWindow]
2573 modifierFlags:[initiatingEvent modifierFlags]
2574 timestamp:[initiatingEvent timestamp]
2575 windowNumber:[initiatingEvent windowNumber]
2576 context:[initiatingEvent context]
2577 characters:[initiatingEvent characters]
2578 charactersIgnoringModifiers:[initiatingEvent charactersIgnoringModifiers]
2579 isARepeat:[initiatingEvent isARepeat]
2580 keyCode:[initiatingEvent keyCode]];
2581 keyEvent(fakeEvent);
2583 // FIXME: We should really get the current modifierFlags here, but there's no way to poll
2584 // them in Cocoa, and because the event stream was stolen by the Carbon menu code we have
2585 // no up-to-date cache of them anywhere.
2586 fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
2587 location:[[_bridge window] convertScreenToBase:[NSEvent mouseLocation]]
2588 modifierFlags:[initiatingEvent modifierFlags]
2589 timestamp:[initiatingEvent timestamp]
2590 windowNumber:[initiatingEvent windowNumber]
2591 context:[initiatingEvent context]
2595 mouseMoved(fakeEvent);
2597 KWQ_UNBLOCK_EXCEPTIONS;
2600 void KWQKHTMLPart::mouseMoved(NSEvent *event)
2602 KHTMLView *v = d->m_view;
2603 // Reject a mouse moved if the button is down - screws up tracking during autoscroll
2604 // These happen because WebKit sometimes has to fake up moved events.
2605 if (!v || d->m_bMousePressed) {
2609 KWQ_BLOCK_EXCEPTIONS;
2611 NSEvent *oldCurrentEvent = _currentEvent;
2612 _currentEvent = KWQRetain(event);
2614 // Sending an event can result in the destruction of the view and part.
2615 // We ref so that happens after we return from the KHTMLView function.
2617 QMouseEvent kEvent(QEvent::MouseMove, event);
2618 v->viewportMouseMoveEvent(&kEvent);
2621 ASSERT(_currentEvent == event);
2623 _currentEvent = oldCurrentEvent;
2625 KWQ_UNBLOCK_EXCEPTIONS;
2628 // Called as we walk up the element chain for nodes with CSS property -khtml-user-drag == auto
2629 bool KWQKHTMLPart::shouldDragAutoNode(DOM::NodeImpl* node, int x, int y) const
2631 // We assume that WebKit only cares about dragging things that can be leaf nodes (text, images, urls).
2632 // This saves a bunch of expensive calls (creating WC and WK element dicts) as we walk farther up
2633 // the node hierarchy, and we also don't have to cook up a way to ask WK about non-leaf nodes
2634 // (since right now WK just hit-tests using a cached lastMouseDown).
2635 if (!node->hasChildNodes() && d->m_view) {
2636 int windowX, windowY;
2637 d->m_view->contentsToViewport(x, y, windowX, windowY);
2638 NSPoint eventLoc = {windowX, windowY};
2639 return [_bridge mayStartDragAtEventLocation:eventLoc];
2645 bool KWQKHTMLPart::sendContextMenuEvent(NSEvent *event)
2647 DocumentImpl *doc = d->m_doc;
2648 KHTMLView *v = d->m_view;
2653 KWQ_BLOCK_EXCEPTIONS;
2655 NSEvent *oldCurrentEvent = _currentEvent;
2656 _currentEvent = KWQRetain(event);
2658 QMouseEvent qev(QEvent::MouseButtonPress, event);
2661 v->viewportToContents(qev.x(), qev.y(), xm, ym);
2663 NodeImpl::MouseEvent mev(qev.stateAfter(), NodeImpl::MousePress);
2664 doc->prepareMouseEvent(false, xm, ym, &mev);
2666 // Sending an event can result in the destruction of the view and part.
2667 // We ref so that happens after we return from the KHTMLView function.
2669 bool swallowEvent = v->dispatchMouseEvent(EventImpl::CONTEXTMENU_EVENT,
2670 mev.innerNode.handle(), true, 0, &qev, true, NodeImpl::MousePress);
2673 ASSERT(_currentEvent == event);
2675 _currentEvent = oldCurrentEvent;
2677 return swallowEvent;
2679 KWQ_UNBLOCK_EXCEPTIONS;
2684 struct ListItemInfo {
2689 NSFileWrapper *KWQKHTMLPart::fileWrapperForElement(ElementImpl *e)
2691 KWQ_BLOCK_EXCEPTIONS;
2693 NSFileWrapper *wrapper = nil;
2695 AtomicString attr = e->getAttribute(ATTR_SRC);
2696 if (!attr.isEmpty()) {
2697 NSURL *URL = completeURL(attr.string()).getNSURL();
2698 wrapper = [_bridge fileWrapperForURL:URL];
2701 RenderImage *renderer = static_cast<RenderImage *>(e->renderer());
2702 NSImage *image = renderer->pixmap().image();
2703 NSData *tiffData = [image TIFFRepresentationUsingCompression:NSTIFFCompressionLZW factor:0.0];
2704 wrapper = [[NSFileWrapper alloc] initRegularFileWithContents:tiffData];
2705 [wrapper setPreferredFilename:@"image.tiff"];
2706 [wrapper autorelease];
2711 KWQ_UNBLOCK_EXCEPTIONS;
2716 static ElementImpl *listParent(ElementImpl *item)
2718 // Ick! Avoid use of item->id() which confuses ObjC++.
2719 unsigned short _id = Node(item).elementId();
2721 while (_id != ID_UL && _id != ID_OL) {
2722 item = static_cast<ElementImpl *>(item->parentNode());
2725 _id = Node(item).elementId();
2730 static NodeImpl* isTextFirstInListItem(NodeImpl *e)
2732 if (Node(e).nodeType() != Node::TEXT_NODE)
2734 NodeImpl* par = e->parentNode();
2736 if (par->firstChild() != e)
2738 if (Node(par).elementId() == ID_LI)
2741 par = par->parentNode();
2746 // FIXME: This collosal function needs to be refactored into maintainable smaller bits.
2748 #define BULLET_CHAR 0x2022
2749 #define SQUARE_CHAR 0x25AA
2750 #define CIRCLE_CHAR 0x25E6
2752 NSAttributedString *KWQKHTMLPart::attributedString(NodeImpl *_start, int startOffset, NodeImpl *endNode, int endOffset)
2754 KWQ_BLOCK_EXCEPTIONS;
2756 NodeImpl * _startNode = _start;
2758 if (_startNode == nil) {
2762 NSMutableAttributedString *result = [[[NSMutableAttributedString alloc] init] autorelease];
2764 bool hasNewLine = true;
2765 bool addedSpace = true;
2766 NSAttributedString *pendingStyledSpace = nil;
2767 bool hasParagraphBreak = true;
2768 const ElementImpl *linkStartNode = 0;
2769 unsigned linkStartLocation = 0;
2770 QPtrList<ElementImpl> listItems;
2771 QValueList<ListItemInfo> listItemLocations;
2772 float maxMarkerWidth = 0;
2774 Node n = _startNode;
2776 // If the first item is the entire text of a list item, use the list item node as the start of the
2777 // selection, not the text node. The user's intent was probably to select the list.
2778 if (n.nodeType() == Node::TEXT_NODE && startOffset == 0) {
2779 NodeImpl *startListNode = isTextFirstInListItem(_startNode);
2781 _startNode = startListNode;
2786 while (!n.isNull()) {
2787 RenderObject *renderer = n.handle()->renderer();
2789 RenderStyle *style = renderer->style();
2790 NSFont *font = style->font().getNSFont();
2791 bool needSpace = pendingStyledSpace != nil;
2792 if (n.nodeType() == Node::TEXT_NODE) {
2796 [pendingStyledSpace release];
2797 pendingStyledSpace = nil;
2801 QString str = n.nodeValue().string();
2802 int start = (n == _startNode) ? startOffset : -1;
2803 int end = (n == endNode) ? endOffset : -1;
2804 if (renderer->isText()) {
2805 if (renderer->style()->whiteSpace() == PRE) {
2806 if (needSpace && !addedSpace) {
2807 if (text.isEmpty() && linkStartLocation == [result length]) {
2808 ++linkStartLocation;
2810 [result appendAttributedString:pendingStyledSpace];
2812 int runStart = (start == -1) ? 0 : start;
2813 int runEnd = (end == -1) ? str.length() : end;
2814 text += str.mid(runStart, runEnd-runStart);
2815 [pendingStyledSpace release];
2816 pendingStyledSpace = nil;
2817 addedSpace = str[runEnd-1].direction() == QChar::DirWS;
2820 RenderText* textObj = static_cast<RenderText*>(renderer);
2821 if (!textObj->firstTextBox() && str.length() > 0 && !addedSpace) {
2822 // We have no runs, but we do have a length. This means we must be
2823 // whitespace that collapsed away at the end of a line.
2829 for (InlineTextBox* box = textObj->firstTextBox(); box; box = box->nextTextBox()) {
2830 int runStart = (start == -1) ? box->m_start : start;
2831 int runEnd = (end == -1) ? box->m_start + box->m_len : end;
2832 runEnd = kMin(runEnd, box->m_start + box->m_len);
2833 if (runStart >= box->m_start &&
2834 runStart < box->m_start + box->m_len) {
2835 if (box == textObj->firstTextBox() && box->m_start == runStart && runStart > 0) {
2836 needSpace = true; // collapsed space at the start
2838 if (needSpace && !addedSpace) {
2839 if (pendingStyledSpace != nil) {
2840 if (text.isEmpty() && linkStartLocation == [result length]) {
2841 ++linkStartLocation;
2843 [result appendAttributedString:pendingStyledSpace];
2848 QString runText = str.mid(runStart, runEnd - runStart);
2849 runText.replace('\n', ' ');
2851 int nextRunStart = box->nextTextBox() ? box->nextTextBox()->m_start : str.length(); // collapsed space between runs or at the end
2852 needSpace = nextRunStart > runEnd;
2853 [pendingStyledSpace release];
2854 pendingStyledSpace = nil;
2855 addedSpace = str[runEnd-1].direction() == QChar::DirWS;
2858 if (end != -1 && runEnd >= end)
2865 text.replace('\\', renderer->backslashAsCurrencySymbol());
2867 if (text.length() > 0 || needSpace) {
2868 NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init];
2869 [attrs setObject:font forKey:NSFontAttributeName];
2870 if (style && style->color().isValid() && qAlpha(style->color().rgb()) != 0)
2871 [attrs setObject:style->color().getNSColor() forKey:NSForegroundColorAttributeName];
2872 if (style && style->backgroundColor().isValid() && qAlpha(style->backgroundColor().rgb()) != 0)
2873 [attrs setObject:style->backgroundColor().getNSColor() forKey:NSBackgroundColorAttributeName];
2875 if (text.length() > 0) {
2876 hasParagraphBreak = false;
2877 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString() attributes:attrs];
2878 [result appendAttributedString: partialString];
2879 [partialString release];
2883 [pendingStyledSpace release];
2884 pendingStyledSpace = [[NSAttributedString alloc] initWithString:@" " attributes:attrs];
2890 // This is our simple HTML -> ASCII transformation:
2892 unsigned short _id = n.elementId();
2895 // Note the start of the <a> element. We will add the NSLinkAttributeName
2896 // attribute to the attributed string when navigating to the next sibling
2898 linkStartLocation = [result length];
2899 linkStartNode = static_cast<ElementImpl*>(n.handle());
2910 ElementImpl *itemParent = listParent(static_cast<ElementImpl *>(n.handle()));
2916 listItems.append(static_cast<ElementImpl*>(n.handle()));
2918 info.start = [result length];
2920 listItemLocations.append (info);
2924 khtml::RenderListItem *listRenderer = static_cast<khtml::RenderListItem*>(renderer);
2926 maxMarkerWidth = MAX([font pointSize], maxMarkerWidth);
2927 switch(listRenderer->style()->listStyleType()) {
2929 listText += ((QChar)BULLET_CHAR);
2932 listText += ((QChar)CIRCLE_CHAR);
2935 listText += ((QChar)SQUARE_CHAR);
2940 QString marker = listRenderer->markerStringValue();
2942 // Use AppKit metrics. Will be rendered by AppKit.
2943 float markerWidth = [font widthOfString: marker.getNSString()];
2944 maxMarkerWidth = MAX(markerWidth, maxMarkerWidth);
2950 NSMutableDictionary *attrs;
2952 attrs = [[NSMutableDictionary alloc] init];
2953 [attrs setObject:font forKey:NSFontAttributeName];
2954 if (style && style->color().isValid())
2955 [attrs setObject:style->color().getNSColor() forKey:NSForegroundColorAttributeName];
2956 if (style && style->backgroundColor().isValid())
2957 [attrs setObject:style->backgroundColor().getNSColor() forKey:NSBackgroundColorAttributeName];
2959 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:listText.getNSString() attributes:attrs];
2961 [result appendAttributedString: partialString];
2962 [partialString release];
2997 if (!hasParagraphBreak) {
2999 hasParagraphBreak = true;
3005 if (pendingStyledSpace != nil) {
3006 if (linkStartLocation == [result length]) {
3007 ++linkStartLocation;
3009 [result appendAttributedString:pendingStyledSpace];
3010 [pendingStyledSpace release];
3011 pendingStyledSpace = nil;
3013 NSFileWrapper *fileWrapper = fileWrapperForElement(static_cast<ElementImpl *>(n.handle()));
3014 NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWrapper:fileWrapper];
3015 NSAttributedString *iString = [NSAttributedString attributedStringWithAttachment:attachment];
3016 [result appendAttributedString: iString];
3017 [attachment release];
3020 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()];
3021 [result appendAttributedString: partialString];
3022 [partialString release];
3029 Node next = n.firstChild();
3031 next = n.nextSibling();
3034 while (next.isNull() && !n.parentNode().isNull()) {
3039 next = n.nextSibling();
3041 unsigned short _id = n.elementId();
3044 // End of a <a> element. Create an attributed string NSLinkAttributeName attribute
3045 // for the range of the link. Note that we create the attributed string from the DOM, which
3046 // will have corrected any illegally nested <a> elements.
3047 if (linkStartNode && n.handle() == linkStartNode){
3048 DOMString href = parseURL(linkStartNode->getAttribute(ATTR_HREF));
3049 KURL kURL = KWQ(linkStartNode->getDocument()->part())->completeURL(href.string());
3051 NSURL *URL = kURL.getNSURL();
3052 [result addAttribute:NSLinkAttributeName value:URL range:NSMakeRange(linkStartLocation, [result length]-linkStartLocation)];
3066 int i, count = listItems.count();
3067 for (i = 0; i < count; i++){
3068 if (listItems.at(i) == n.handle()){
3069 listItemLocations[i].end = [result length];
3102 // An extra newline is needed at the start, not the end, of these types of tags,
3103 // so don't add another here.
3108 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()];
3109 [result appendAttributedString:partialString];
3110 [partialString release];
3116 [pendingStyledSpace release];
3118 // Apply paragraph styles from outside in. This ensures that nested lists correctly
3119 // override their parent's paragraph style.
3121 unsigned i, count = listItems.count();
3125 #ifdef POSITION_LIST
3126 NodeImpl *containingBlock;
3127 int containingBlockX, containingBlockY;
3129 // Determine the position of the outermost containing block. All paragraph
3130 // styles and tabs should be relative to this position. So, the horizontal position of
3131 // each item in the list (in the resulting attributed string) will be relative to position
3132 // of the outermost containing block.
3134 containingBlock = _startNode;
3135 while (containingBlock->renderer()->isInline()){
3136 containingBlock = containingBlock->parentNode();
3138 containingBlock->renderer()->absolutePosition(containingBlockX, containingBlockY);
3142 for (i = 0; i < count; i++){
3143 e = listItems.at(i);
3144 info = listItemLocations[i];
3146 if (info.end < info.start)
3147 info.end = [result length];
3149 RenderObject *r = e->renderer();
3150 RenderStyle *style = r->style();
3153 NSFont *font = style->font().getNSFont();
3154 float pointSize = [font pointSize];
3156 #ifdef POSITION_LIST
3158 r->absolutePosition(rx, ry);
3159 rx -= containingBlockX;
3161 // Ensure that the text is indented at least enough to allow for the markers.
3162 rx = MAX(rx, (int)maxMarkerWidth);
3164 rx = (int)MAX(maxMarkerWidth, pointSize);
3167 // The bullet text will be right aligned at the first tab marker, followed
3168 // by a space, followed by the list item text. The space is arbitrarily
3169 // picked as pointSize*2/3. The space on the first line of the text item
3170 // is established by a left aligned tab, on subsequent lines it's established
3171 // by the head indent.
3172 NSMutableParagraphStyle *mps = [[NSMutableParagraphStyle alloc] init];
3173 [mps setFirstLineHeadIndent: 0];
3174 [mps setHeadIndent: rx];
3175 [mps setTabStops:[NSArray arrayWithObjects:
3176 [[[NSTextTab alloc] initWithType:NSRightTabStopType location:rx-(pointSize*2/3)] autorelease],
3177 [[[NSTextTab alloc] initWithType:NSLeftTabStopType location:rx] autorelease],
3179 [result addAttribute:NSParagraphStyleAttributeName value:mps range:NSMakeRange(info.start, info.end-info.start)];
3186 KWQ_UNBLOCK_EXCEPTIONS;
3191 QRect KWQKHTMLPart::selectionRect() const
3197 RenderCanvas *root = static_cast<RenderCanvas *>(xmlDocImpl()->renderer());
3202 return root->selectionRect();
3205 // returns NSRect because going through QRect would truncate any floats
3206 NSRect KWQKHTMLPart::visibleSelectionRect() const
3211 NSView *documentView = d->m_view->getDocumentView();
3212 if (!documentView) {
3215 return NSIntersectionRect(selectionRect(), [documentView visibleRect]);
3218 NSImage *KWQKHTMLPart::imageFromRect(NSRect rect) const
3220 NSView *view = d->m_view->getDocumentView();
3225 NSRect bounds = [view bounds];
3226 NSImage *resultImage = [[[NSImage alloc] initWithSize:rect.size] autorelease];
3228 KWQ_BLOCK_EXCEPTIONS;
3230 [resultImage setFlipped:YES];
3231 [resultImage lockFocus];
3233 [NSGraphicsContext saveGraphicsState];
3234 NSPoint translation = { -(NSMinX(rect) - NSMinX(bounds)), -(NSMinY(rect) - NSMinY(bounds)) };
3235 CGContextTranslateCTM((CGContext *)[[NSGraphicsContext currentContext] graphicsPort], translation.x, translation.y);
3237 // We change the coord system at the CG level, out from under the AK focus machinery, because it doesn't
3238 // work to change the coord system of a focused view. However, WebImageRenderer uses the difference
3239 // between the focused view's coord system and the window's coord system to adjust the pattern phase, and
3240 // that calc ignores our translation. So we must tell it about this extra phase offset.
3242 // Window is not flipped, we are, so y coord must be inverted when describing phase, which is a
3243 // window level notion.
3244 translation.y = -translation.y;
3245 [[WebCoreGraphicsBridge sharedBridge] setAdditionalPatternPhase:translation];
3247 [view drawRect:rect];
3249 [[WebCoreGraphicsBridge sharedBridge] setAdditionalPatternPhase:NSZeroPoint];
3250 [NSGraphicsContext restoreGraphicsState];
3252 [resultImage unlockFocus];
3253 [resultImage setFlipped:NO];
3255 KWQ_UNBLOCK_EXCEPTIONS;
3260 NSImage *KWQKHTMLPart::selectionImage() const
3262 _drawSelectionOnly = true; // invoke special drawing mode
3263 NSImage *result = imageFromRect(visibleSelectionRect());
3264 _drawSelectionOnly = false;
3268 NSImage *KWQKHTMLPart::snapshotDragImage(DOM::Node node, NSRect *imageRect, NSRect *elementRect) const
3270 RenderObject *renderer = node.handle()->renderer();
3275 renderer->updateDragState(true); // mark dragged nodes (so they pick up the right CSS)
3276 d->m_doc->updateLayout(); // forces style recalc - needed since changing the drag state might
3277 // imply new styles, plus JS could have changed other things
3279 NSRect paintingRect = renderer->paintingRootRect(topLevelRect);
3281 _elementToDraw = node; // invoke special sub-tree drawing mode
3282 NSImage *result = imageFromRect(paintingRect);
3283 renderer->updateDragState(false);
3287 *elementRect = topLevelRect;
3290 *imageRect = paintingRect;
3295 NSFont *KWQKHTMLPart::fontForCurrentPosition() const
3297 if (d->m_selection.state() == Selection::NONE)
3300 Range range(d->m_selection.toRange());
3301 Position pos(range.startContainer().handle(), range.startOffset());
3302 ASSERT(pos.notEmpty());
3303 ElementImpl *elem = pos.element();
3304 if (d->m_typingStyle) {
3308 int exceptionCode = 0;
3309 ElementImpl *styleElement = xmlDocImpl()->createHTMLElement("SPAN", exceptionCode);
3310 ASSERT(exceptionCode == 0);
3312 styleElement->setAttribute(ATTR_STYLE, d->m_typingStyle->cssText().implementation(), exceptionCode);
3313 ASSERT(exceptionCode == 0);
3315 TextImpl *text = xmlDocImpl()->createEditingTextNode("");
3316 styleElement->appendChild(text, exceptionCode);
3317 ASSERT(exceptionCode == 0);
3319 elem->appendChild(styleElement, exceptionCode);
3320 ASSERT(exceptionCode == 0);
3322 RenderObject *renderer = styleElement->renderer();
3324 NSFont *result = renderer->style()->font().getNSFont();
3326 styleElement->removeChild(text, exceptionCode);
3327 ASSERT(exceptionCode == 0);
3329 elem->removeChild(styleElement, exceptionCode);
3330 ASSERT(exceptionCode == 0);
3335 RenderObject *renderer = elem->renderer();
3337 return renderer->style()->font().getNSFont();
3342 KWQWindowWidget *KWQKHTMLPart::topLevelWidget()
3344 return _windowWidget;
3347 int KWQKHTMLPart::selectionStartOffset() const
3349 return d->m_selection.start().offset();
3352 int KWQKHTMLPart::selectionEndOffset() const
3354 return d->m_selection.end().offset();
3357 NodeImpl *KWQKHTMLPart::selectionStart() const
3359 return d->m_selection.start().node();
3362 NodeImpl *KWQKHTMLPart::selectionEnd() const
3364 return d->m_selection.end().node();
3367 void KWQKHTMLPart::setBridge(WebCoreBridge *p)
3370 delete _windowWidget;
3373 _windowWidget = new KWQWindowWidget(_bridge);
3376 QString KWQKHTMLPart::overrideMediaType()
3378 NSString *overrideType = [_bridge overrideMediaType];
3380 return QString::fromNSString(overrideType);
3384 void KWQKHTMLPart::setMediaType(const QString &type)
3387 d->m_view->setMediaType(type);
3391 void KWQKHTMLPart::setDisplaysWithFocusAttributes(bool flag)
3393 if (_displaysWithFocusAttributes == flag)
3395 _displaysWithFocusAttributes = flag;
3397 // This method does the job of updating the view based on whether the view is "active".
3398 // This involves three kinds of drawing updates:
3400 // 1. The background color used to draw behind selected content (active | inactive color)
3402 d->m_view->updateContents(QRect(visibleSelectionRect()));
3404 // 2. Caret blinking (blinks | does not blink)
3405 setCaretVisible(flag);
3407 // 3. The drawing of a focus ring around links in web pages.
3408 DocumentImpl *doc = xmlDocImpl();
3410 NodeImpl *node = doc->focusNode();
3411 if (node && node->renderer())
3412 node->renderer()->repaint();
3416 QChar KWQKHTMLPart::backslashAsCurrencySymbol() const
3418 DocumentImpl *doc = xmlDocImpl();
3422 Decoder *decoder = doc->decoder();
3426 const QTextCodec *codec = decoder->codec();
3430 return codec->backslashAsCurrencySymbol();
3433 NSColor *KWQKHTMLPart::bodyBackgroundColor() const
3435 if (docImpl() && docImpl()->body() && docImpl()->body()->renderer()) {
3436 QColor bgColor = docImpl()->body()->renderer()->style()->backgroundColor();
3437 if (bgColor.isValid()) {
3438 return bgColor.getNSColor();
3444 WebCoreKeyboardUIMode KWQKHTMLPart::keyboardUIMode() const
3446 KWQ_BLOCK_EXCEPTIONS;
3447 return [_bridge keyboardUIMode];
3448 KWQ_UNBLOCK_EXCEPTIONS;
3450 return WebCoreKeyboardAccessDefault;
3453 void KWQKHTMLPart::setName(const QString &name)
3457 KWQKHTMLPart *parent = KWQ(parentPart());
3459 if (parent && (name.isEmpty() || parent->frameExists(name))) {
3460 n = parent->requestFrameName();
3463 KHTMLPart::setName(n);
3465 KWQ_BLOCK_EXCEPTIONS;
3466 [_bridge didSetName:n.getNSString()];
3467 KWQ_UNBLOCK_EXCEPTIONS;
3471 void KWQKHTMLPart::didTellBridgeAboutLoad(const QString &urlString)
3473 urlsBridgeKnowsAbout.insert(urlString, (char *)1);
3477 bool KWQKHTMLPart::haveToldBridgeAboutLoad(const QString &urlString)
3479 return urlsBridgeKnowsAbout.find(urlString) != 0;
3482 void KWQKHTMLPart::clear()
3484 urlsBridgeKnowsAbout.clear();
3488 void KHTMLPart::print()
3490 [KWQ(this)->_bridge print];
3493 KJS::Bindings::Instance *KWQKHTMLPart::getAppletInstanceForView (NSView *aView)
3495 // Get a pointer to the actual Java applet instance.
3496 jobject applet = [_bridge pollForAppletInView:aView];
3499 // Wrap the Java instance in a language neutral binding and hand
3500 // off ownership to the APPLET element.
3501 return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::JavaLanguage, applet);
3506 @interface NSObject (WebPlugIn)
3507 - (id)objectForWebScript;
3508 - (void *)pluginScriptableObject;
3511 KJS::Bindings::Instance *KWQKHTMLPart::getEmbedInstanceForView (NSView *aView)
3513 if ([aView respondsToSelector:@selector(objectForWebScript)]){
3514 id object = [aView objectForWebScript];
3516 return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::ObjectiveCLanguage, object);
3518 else if ([aView respondsToSelector:@selector(pluginScriptableObject)]){
3519 void *object = [aView pluginScriptableObject];
3521 return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::CLanguage, object);
3526 void KWQKHTMLPart::addPluginRootObject(const KJS::Bindings::RootObject *root)
3528 rootObjects.append (root);
3531 void KWQKHTMLPart::cleanupPluginRootObjects()
3533 KJS::Bindings::RootObject *root;
3534 while ((root = rootObjects.getLast())) {
3535 root->removeAllNativeReferences ();
3536 rootObjects.removeLast();
3540 void KWQKHTMLPart::registerCommandForUndo(const khtml::EditCommand &cmd)
3542 ASSERT(cmd.handle());
3543 KWQEditCommand *kwq = [KWQEditCommand commandWithEditCommandImpl:cmd.handle()];
3544 [[_bridge undoManager] registerUndoWithTarget:_bridge selector:@selector(undoEditing:) object:kwq];
3545 _haveUndoRedoOperations = YES;
3548 void KWQKHTMLPart::registerCommandForRedo(const khtml::EditCommand &cmd)
3550 ASSERT(cmd.handle());
3551 KWQEditCommand *kwq = [KWQEditCommand commandWithEditCommandImpl:cmd.handle()];
3552 [[_bridge undoManager] registerUndoWithTarget:_bridge selector:@selector(redoEditing:) object:kwq];
3553 _haveUndoRedoOperations = YES;
3556 void KWQKHTMLPart::clearUndoRedoOperations()
3558 if (_haveUndoRedoOperations) {
3559 [[_bridge undoManager] removeAllActionsWithTarget:_bridge];
3560 _haveUndoRedoOperations = NO;
3564 void KWQKHTMLPart::issueUndoCommand()
3567 [[_bridge undoManager] undo];
3570 void KWQKHTMLPart::issueRedoCommand()
3573 [[_bridge undoManager] redo];
3576 void KWQKHTMLPart::issueCutCommand()
3578 [_bridge issueCutCommand];
3581 void KWQKHTMLPart::issueCopyCommand()
3583 [_bridge issueCopyCommand];
3586 void KWQKHTMLPart::issuePasteCommand()
3588 [_bridge issuePasteCommand];
3591 bool KHTMLPart::canUndo() const
3593 return [[KWQ(this)->_bridge undoManager] canUndo];
3596 bool KHTMLPart::canRedo() const
3598 return [[KWQ(this)->_bridge undoManager] canRedo];
3601 void KWQKHTMLPart::respondToChangedSelection()
3603 [_bridge respondToChangedSelection];
3606 void KWQKHTMLPart::respondToChangedContents()
3608 [_bridge respondToChangedContents];
3611 bool KWQKHTMLPart::isContentEditable() const
3613 return [_bridge isEditable];
3616 bool KWQKHTMLPart::shouldBeginEditing(const Range &range) const
3618 ASSERT(!range.isNull());
3619 return [_bridge shouldBeginEditing:[DOMRange _rangeWithImpl:range.handle()]];
3622 bool KWQKHTMLPart::shouldEndEditing(const Range &range) const
3624 ASSERT(!range.isNull());
3625 return [_bridge shouldEndEditing:[DOMRange _rangeWithImpl:range.handle()]];