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"
30 #import "KWQClipboard.h"
31 #import "KWQDOMNode.h"
32 #import "KWQDummyView.h"
33 #import "KWQEditCommand.h"
34 #import "KWQExceptions.h"
35 #import "KWQFormData.h"
36 #import "KWQFoundationExtras.h"
37 #import "KWQKJobClasses.h"
38 #import "KWQLogging.h"
39 #import "KWQPageState.h"
40 #import "KWQPrinter.h"
42 #import "KWQScrollBar.h"
43 #import "KWQWindowWidget.h"
45 #import "WebCoreBridge.h"
46 #import "WebCoreGraphicsBridge.h"
47 #import "WebCoreViewFactory.h"
48 #import "WebDashboardRegion.h"
50 #import "css_computedstyle.h"
52 #import "dom2_eventsimpl.h"
53 #import "dom2_rangeimpl.h"
54 #import "dom_position.h"
55 #import "dom_textimpl.h"
56 #import "html_document.h"
57 #import "html_documentimpl.h"
58 #import "html_formimpl.h"
60 #import "html_tableimpl.h"
62 #import "htmltokenizer.h"
63 #import "khtmlpart_p.h"
65 #import "kjs_binding.h"
66 #import "kjs_window.h"
67 #import "render_canvas.h"
68 #import "render_frames.h"
69 #import "render_image.h"
70 #import "render_list.h"
71 #import "render_style.h"
72 #import "render_table.h"
73 #import "render_text.h"
75 #import "visible_position.h"
76 #import "visible_text.h"
77 #import "visible_units.h"
79 #import <JavaScriptCore/identifier.h>
80 #import <JavaScriptCore/property_map.h>
81 #import <JavaScriptCore/runtime.h>
82 #import <JavaScriptCore/runtime_root.h>
83 #import <JavaScriptCore/WebScriptObjectPrivate.h>
86 #import "KWQAccObjectCache.h"
91 using DOM::AtomicString;
92 using DOM::ClipboardEventImpl;
93 using DOM::DocumentFragmentImpl;
94 using DOM::DocumentImpl;
95 using DOM::DocumentMarker;
97 using DOM::ElementImpl;
99 using DOM::HTMLDocumentImpl;
100 using DOM::HTMLElementImpl;
101 using DOM::HTMLFormElementImpl;
102 using DOM::HTMLFrameElementImpl;
103 using DOM::HTMLGenericFormElementImpl;
104 using DOM::HTMLTableCellElementImpl;
109 using DOM::RangeImpl;
113 using khtml::CharacterIterator;
114 using khtml::ChildFrame;
115 using khtml::Decoder;
116 using khtml::DashboardRegionValue;
117 using khtml::EditCommandPtr;
118 using khtml::endOfWord;
119 using khtml::findPlainText;
120 using khtml::InlineTextBox;
121 using khtml::LeftWordIfOnBoundary;
122 using khtml::MouseDoubleClickEvent;
123 using khtml::MouseMoveEvent;
124 using khtml::MousePressEvent;
125 using khtml::MouseReleaseEvent;
126 using khtml::parseURL;
128 using khtml::RenderCanvas;
129 using khtml::RenderImage;
130 using khtml::RenderLayer;
131 using khtml::RenderListItem;
132 using khtml::RenderObject;
133 using khtml::RenderPart;
134 using khtml::RenderStyle;
135 using khtml::RenderTableCell;
136 using khtml::RenderText;
137 using khtml::RenderWidget;
138 using khtml::RightWordIfOnBoundary;
139 using khtml::Selection;
141 using khtml::setStart;
142 using khtml::ShadowData;
143 using khtml::startOfWord;
144 using khtml::startVisiblePosition;
145 using khtml::StyleDashboardRegion;
146 using khtml::TextIterator;
147 using khtml::UPSTREAM;
148 using khtml::VISIBLE;
149 using khtml::VisiblePosition;
150 using khtml::WordAwareIterator;
154 using KJS::Interpreter;
156 using KJS::SavedBuiltins;
157 using KJS::SavedProperties;
158 using KJS::ScheduledAction;
161 using KJS::Bindings::Instance;
163 using KParts::ReadOnlyPart;
164 using KParts::URLArgs;
166 NSEvent *KWQKHTMLPart::_currentEvent = nil;
168 void KHTMLPart::completed()
170 KWQ(this)->_completed.call();
173 void KHTMLPart::completed(bool arg)
175 KWQ(this)->_completed.call(arg);
178 void KHTMLPart::nodeActivated(const Node &)
182 bool KHTMLPart::openURL(const KURL &URL)
184 ASSERT_NOT_REACHED();
188 void KHTMLPart::onURL(const QString &)
192 void KHTMLPart::setStatusBarText(const QString &status)
194 KWQ(this)->setStatusBarText(status);
197 void KHTMLPart::started(Job *j)
199 KWQ(this)->_started.call(j);
202 bool KHTMLView::isKHTMLView() const
207 static void redirectionTimerMonitor(void *context)
209 KWQKHTMLPart *kwq = static_cast<KWQKHTMLPart *>(context);
210 kwq->redirectionTimerStartedOrStopped();
213 KWQKHTMLPart::KWQKHTMLPart()
214 : _started(this, SIGNAL(started(KIO::Job *)))
215 , _completed(this, SIGNAL(completed()))
216 , _completedWithBool(this, SIGNAL(completed(bool)))
217 , _mouseDownView(nil)
218 , _sendingEventToSubview(false)
219 , _mouseDownMayStartDrag(false)
220 , _mouseDownMayStartSelect(false)
221 , _activationEventNumber(0)
222 , _formValuesAboutToBeSubmitted(nil)
223 , _formAboutToBeSubmitted(nil)
224 , _windowWidget(NULL)
225 , _drawSelectionOnly(false)
227 , _windowScriptObject(0)
228 , _windowScriptNPObject(0)
233 // Must init the cache before connecting to any signals
236 // The widget is made outside this class in our case.
237 KHTMLPart::init( 0, DefaultGUI );
239 mutableInstances().prepend(this);
240 d->m_redirectionTimer.setMonitor(redirectionTimerMonitor, this);
243 KWQKHTMLPart::~KWQKHTMLPart()
245 cleanupPluginRootObjects();
247 mutableInstances().remove(this);
252 // these are all basic Foundation classes and our own classes - we
253 // know they will not raise in dealloc, so no need to block
255 KWQRelease(_formValuesAboutToBeSubmitted);
256 KWQRelease(_formAboutToBeSubmitted);
258 KWQRelease(_windowScriptObject);
260 delete _windowWidget;
263 void KWQKHTMLPart::freeClipboard()
265 if (_dragClipboard) {
266 _dragClipboard->setAccessPolicy(KWQClipboard::Numb);
267 _dragClipboard->deref();
272 void KWQKHTMLPart::setSettings (KHTMLSettings *settings)
274 d->m_settings = settings;
277 QString KWQKHTMLPart::generateFrameName()
279 KWQ_BLOCK_EXCEPTIONS;
280 return QString::fromNSString([_bridge generateFrameName]);
281 KWQ_UNBLOCK_EXCEPTIONS;
286 void KWQKHTMLPart::provisionalLoadStarted()
288 // we don't want to wait until we get an actual http response back
289 // to cancel pending redirects, otherwise they might fire before
291 cancelRedirection(true);
294 bool KWQKHTMLPart::openURL(const KURL &url)
296 KWQ_BLOCK_EXCEPTIONS;
298 bool userGesture = true;
300 if (jScript() && jScript()->interpreter()) {
301 KHTMLPart *rootPart = this;
302 while (rootPart->parentPart() != 0)
303 rootPart = rootPart->parentPart();
304 KJS::ScriptInterpreter *interpreter = static_cast<KJS::ScriptInterpreter *>(KJSProxy::proxy(rootPart)->interpreter());
305 userGesture = interpreter->wasRunByUserGesture();
308 // FIXME: The lack of args here to get the reload flag from
309 // indicates a problem in how we use KHTMLPart::processObjectRequest,
310 // where we are opening the URL before the args are set up.
311 [_bridge loadURL:url.getNSURL()
312 referrer:[_bridge referrer]
314 userGesture:userGesture
320 KWQ_UNBLOCK_EXCEPTIONS;
325 void KWQKHTMLPart::openURLRequest(const KURL &url, const URLArgs &args)
327 KWQ_BLOCK_EXCEPTIONS;
329 [_bridge loadURL:url.getNSURL()
330 referrer:[_bridge referrer]
333 target:args.frameName.getNSString()
338 KWQ_UNBLOCK_EXCEPTIONS;
341 void KWQKHTMLPart::didNotOpenURL(const KURL &URL)
343 if (_submittedFormURL == URL) {
344 _submittedFormURL = KURL();
348 // Scans logically forward from "start", including any child frames
349 static HTMLFormElementImpl *scanForForm(NodeImpl *start)
352 for (n = start; n; n = n->traverseNextNode()) {
353 NodeImpl::Id nodeID = idFromNode(n);
354 if (nodeID == ID_FORM) {
355 return static_cast<HTMLFormElementImpl *>(n);
356 } else if (n->isHTMLElement()
357 && static_cast<HTMLElementImpl *>(n)->isGenericFormElement()) {
358 return static_cast<HTMLGenericFormElementImpl *>(n)->form();
359 } else if (nodeID == ID_FRAME || nodeID == ID_IFRAME) {
360 NodeImpl *childDoc = static_cast<HTMLFrameElementImpl *>(n)->contentDocument();
361 HTMLFormElementImpl *frameResult = scanForForm(childDoc);
370 // We look for either the form containing the current focus, or for one immediately after it
371 HTMLFormElementImpl *KWQKHTMLPart::currentForm() const
373 // start looking either at the active (first responder) node, or where the selection is
374 NodeImpl *start = activeNode().handle();
376 start = selectionStart();
379 // try walking up the node tree to find a form element
381 for (n = start; n; n = n->parentNode()) {
382 if (idFromNode(n) == ID_FORM) {
383 return static_cast<HTMLFormElementImpl *>(n);
384 } else if (n->isHTMLElement()
385 && static_cast<HTMLElementImpl *>(n)->isGenericFormElement()) {
386 return static_cast<HTMLGenericFormElementImpl *>(n)->form();
390 // try walking forward in the node tree to find a form element
391 return start ? scanForForm(start) : 0;
394 // Either get cached regexp or build one that matches any of the labels.
395 // The regexp we build is of the form: (STR1|STR2|STRN)
396 QRegExp *regExpForLabels(NSArray *labels)
398 // All the ObjC calls in this method are simple array and string
399 // calls which we can assume do not raise exceptions
402 // Parallel arrays that we use to cache regExps. In practice the number of expressions
403 // that the app will use is equal to the number of locales is used in searching.
404 static const unsigned int regExpCacheSize = 4;
405 static NSMutableArray *regExpLabels = nil;
406 static QPtrList <QRegExp> regExps;
407 static QRegExp wordRegExp = QRegExp("\\w");
411 regExpLabels = [[NSMutableArray alloc] initWithCapacity:regExpCacheSize];
413 unsigned int cacheHit = [regExpLabels indexOfObject:labels];
414 if (cacheHit != NSNotFound) {
415 result = regExps.at(cacheHit);
417 QString pattern("(");
418 unsigned int numLabels = [labels count];
420 for (i = 0; i < numLabels; i++) {
421 QString label = QString::fromNSString([labels objectAtIndex:i]);
423 bool startsWithWordChar = false;
424 bool endsWithWordChar = false;
425 if (label.length() != 0) {
426 startsWithWordChar = wordRegExp.search(label.at(0)) >= 0;
427 endsWithWordChar = wordRegExp.search(label.at(label.length() - 1)) >= 0;
433 // Search for word boundaries only if label starts/ends with "word characters".
434 // If we always searched for word boundaries, this wouldn't work for languages
436 if (startsWithWordChar) {
437 pattern.append("\\b");
439 pattern.append(label);
440 if (endsWithWordChar) {
441 pattern.append("\\b");
445 result = new QRegExp(pattern, false);
448 // add regexp to the cache, making sure it is at the front for LRU ordering
450 if (cacheHit != NSNotFound) {
451 // remove from old spot
452 [regExpLabels removeObjectAtIndex:cacheHit];
453 regExps.remove(cacheHit);
456 [regExpLabels insertObject:labels atIndex:0];
457 regExps.insert(0, result);
459 if ([regExpLabels count] > regExpCacheSize) {
460 [regExpLabels removeObjectAtIndex:regExpCacheSize];
461 QRegExp *last = regExps.last();
462 regExps.removeLast();
469 NSString *KWQKHTMLPart::searchForLabelsAboveCell(QRegExp *regExp, HTMLTableCellElementImpl *cell)
471 RenderTableCell *cellRenderer = static_cast<RenderTableCell *>(cell->renderer());
472 RenderTableCell *cellAboveRenderer = cellRenderer->table()->cellAbove(cellRenderer);
474 if (cellAboveRenderer) {
475 HTMLTableCellElementImpl *aboveCell =
476 static_cast<HTMLTableCellElementImpl *>(cellAboveRenderer->element());
479 // search within the above cell we found for a match
480 for (NodeImpl *n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) {
481 if (idFromNode(n) == ID_TEXT
482 && n->renderer() && n->renderer()->style()->visibility() == VISIBLE)
484 // For each text chunk, run the regexp
485 QString nodeString = n->nodeValue().string();
486 int pos = regExp->searchRev(nodeString);
488 return nodeString.mid(pos, regExp->matchedLength()).getNSString();
494 // Any reason in practice to search all cells in that are above cell?
498 NSString *KWQKHTMLPart::searchForLabelsBeforeElement(NSArray *labels, ElementImpl *element)
500 QRegExp *regExp = regExpForLabels(labels);
501 // We stop searching after we've seen this many chars
502 const unsigned int charsSearchedThreshold = 500;
503 // This is the absolute max we search. We allow a little more slop than
504 // charsSearchedThreshold, to make it more likely that we'll search whole nodes.
505 const unsigned int maxCharsSearched = 600;
506 // If the starting element is within a table, the cell that contains it
507 HTMLTableCellElementImpl *startingTableCell = 0;
508 bool searchedCellAbove = false;
510 // walk backwards in the node tree, until another element, or form, or end of tree
511 int unsigned lengthSearched = 0;
513 for (n = element->traversePreviousNode();
514 n && lengthSearched < charsSearchedThreshold;
515 n = n->traversePreviousNode())
517 NodeImpl::Id nodeID = idFromNode(n);
518 if (nodeID == ID_FORM
519 || (n->isHTMLElement()
520 && static_cast<HTMLElementImpl *>(n)->isGenericFormElement()))
522 // We hit another form element or the start of the form - bail out
524 } else if (nodeID == ID_TD && !startingTableCell) {
525 startingTableCell = static_cast<HTMLTableCellElementImpl *>(n);
526 } else if (nodeID == ID_TR && startingTableCell) {
527 NSString *result = searchForLabelsAboveCell(regExp, startingTableCell);
531 searchedCellAbove = true;
532 } else if (nodeID == ID_TEXT
533 && n->renderer() && n->renderer()->style()->visibility() == VISIBLE)
535 // For each text chunk, run the regexp
536 QString nodeString = n->nodeValue().string();
537 // add 100 for slop, to make it more likely that we'll search whole nodes
538 if (lengthSearched + nodeString.length() > maxCharsSearched) {
539 nodeString = nodeString.right(charsSearchedThreshold - lengthSearched);
541 int pos = regExp->searchRev(nodeString);
543 return nodeString.mid(pos, regExp->matchedLength()).getNSString();
545 lengthSearched += nodeString.length();
550 // If we started in a cell, but bailed because we found the start of the form or the
551 // previous element, we still might need to search the row above us for a label.
552 if (startingTableCell && !searchedCellAbove) {
553 return searchForLabelsAboveCell(regExp, startingTableCell);
559 NSString *KWQKHTMLPart::matchLabelsAgainstElement(NSArray *labels, ElementImpl *element)
561 QString name = element->getAttribute(ATTR_NAME).string();
562 // Make numbers and _'s in field names behave like word boundaries, e.g., "address2"
563 name.replace(QRegExp("[[:digit:]]"), " ");
564 name.replace('_', ' ');
566 QRegExp *regExp = regExpForLabels(labels);
567 // Use the largest match we can find in the whole name string
574 pos = regExp->search(name, start);
576 length = regExp->matchedLength();
577 if (length >= bestLength) {
586 return name.mid(bestPos, bestLength).getNSString();
592 // Search from the end of the currently selected location if we are first responder, or from
593 // the beginning of the document if nothing is selected or we're not first responder.
594 bool KWQKHTMLPart::findString(NSString *string, bool forward, bool caseFlag, bool wrapFlag)
596 QString target = QString::fromNSString(string);
597 if (target.isEmpty()) {
601 // Start on the correct edge of the selection, search to edge of document.
602 Range searchRange(xmlDocImpl());
603 searchRange.selectNodeContents(xmlDocImpl());
604 if (selectionStart()) {
606 setStart(searchRange, VisiblePosition(selection().end()));
608 setEnd(searchRange, VisiblePosition(selection().start()));
612 // Do the search once, then do it a second time to handle wrapped search.
613 // Searches some or all of document twice in the failure case, but that's probably OK.
614 Range resultRange = findPlainText(searchRange, target, forward, caseFlag);
615 if (resultRange.collapsed() && wrapFlag) {
616 searchRange.selectNodeContents(xmlDocImpl());
617 resultRange = findPlainText(searchRange, target, forward, caseFlag);
618 // If we got back to the same place we started, that doesn't count as success.
619 if (resultRange == selection().toRange()) {
624 if (resultRange.collapsed()) {
628 setSelection(resultRange);
633 void KWQKHTMLPart::clearRecordedFormValues()
635 // It's safe to assume that our own classes and Foundation data
636 // structures won't raise exceptions in dealloc
638 KWQRelease(_formValuesAboutToBeSubmitted);
639 _formValuesAboutToBeSubmitted = nil;
640 KWQRelease(_formAboutToBeSubmitted);
641 _formAboutToBeSubmitted = nil;
644 void KWQKHTMLPart::recordFormValue(const QString &name, const QString &value, HTMLFormElementImpl *element)
646 // It's safe to assume that our own classes and basic Foundation
647 // data structures won't raise exceptions
649 if (!_formValuesAboutToBeSubmitted) {
650 _formValuesAboutToBeSubmitted = KWQRetainNSRelease([[NSMutableDictionary alloc] init]);
651 ASSERT(!_formAboutToBeSubmitted);
652 _formAboutToBeSubmitted = KWQRetain([DOMElement _elementWithImpl:element]);
654 ASSERT([_formAboutToBeSubmitted _elementImpl] == element);
656 [_formValuesAboutToBeSubmitted setObject:value.getNSString() forKey:name.getNSString()];
659 void KWQKHTMLPart::submitForm(const KURL &url, const URLArgs &args)
661 KWQ_BLOCK_EXCEPTIONS;
663 // FIXME: We'd like to remove this altogether and fix the multiple form submission issue another way.
664 // We do not want to submit more than one form from the same page,
665 // nor do we want to submit a single form more than once.
666 // This flag prevents these from happening; not sure how other browsers prevent this.
667 // The flag is reset in each time we start handle a new mouse or key down event, and
668 // also in setView since this part may get reused for a page from the back/forward cache.
669 // The form multi-submit logic here is only needed when we are submitting a form that affects this frame.
670 // FIXME: Frame targeting is only one of the ways the submission could end up doing something other
671 // than replacing this frame's content, so this check is flawed. On the other hand, the check is hardly
672 // needed any more now that we reset _submittedFormURL on each mouse or key down event.
673 WebCoreBridge *target = args.frameName.isEmpty() ? _bridge : [_bridge findFrameNamed:args.frameName.getNSString()];
674 KHTMLPart *targetPart = [target part];
675 bool willReplaceThisFrame = false;
676 for (KHTMLPart *p = this; p; p = p->parentPart()) {
677 if (p == targetPart) {
678 willReplaceThisFrame = true;
682 if (willReplaceThisFrame) {
683 if (_submittedFormURL == url) {
686 _submittedFormURL = url;
689 if (!args.doPost()) {
690 [_bridge loadURL:url.getNSURL()
691 referrer:[_bridge referrer]
694 target:args.frameName.getNSString()
695 triggeringEvent:_currentEvent
696 form:_formAboutToBeSubmitted
697 formValues:_formValuesAboutToBeSubmitted];
699 ASSERT(args.contentType().startsWith("Content-Type: "));
700 [_bridge postWithURL:url.getNSURL()
701 referrer:[_bridge referrer]
702 target:args.frameName.getNSString()
703 data:arrayFromFormData(args.postData)
704 contentType:args.contentType().mid(14).getNSString()
705 triggeringEvent:_currentEvent
706 form:_formAboutToBeSubmitted
707 formValues:_formValuesAboutToBeSubmitted];
709 clearRecordedFormValues();
711 KWQ_UNBLOCK_EXCEPTIONS;
714 void KWQKHTMLPart::setEncoding(const QString &name, bool userChosen)
716 if (!d->m_workingURL.isEmpty()) {
719 d->m_encoding = name;
720 d->m_haveEncoding = userChosen;
723 void KWQKHTMLPart::addData(const char *bytes, int length)
725 ASSERT(d->m_workingURL.isEmpty());
727 ASSERT(d->m_doc->parsing());
728 write(bytes, length);
731 void KHTMLPart::frameDetached()
733 // FIXME: This should be a virtual function, with the first part in KWQKHTMLPart, and the second
734 // part in KHTMLPart, so it works for KHTML too.
736 KWQ_BLOCK_EXCEPTIONS;
737 [KWQ(this)->bridge() frameDetached];
738 KWQ_UNBLOCK_EXCEPTIONS;
740 KHTMLPart *parent = parentPart();
742 FrameList& parentFrames = parent->d->m_frames;
743 FrameIt end = parentFrames.end();
744 for (FrameIt it = parentFrames.begin(); it != end; ++it) {
745 ChildFrame &child = *it;
746 if (child.m_part == this) {
747 parent->disconnectChild(&child);
748 parentFrames.remove(it);
756 void KWQKHTMLPart::urlSelected(const KURL &url, int button, int state, const URLArgs &args)
758 KWQ_BLOCK_EXCEPTIONS;
759 [_bridge loadURL:url.getNSURL()
760 referrer:[_bridge referrer]
763 target:args.frameName.getNSString()
764 triggeringEvent:_currentEvent
767 KWQ_UNBLOCK_EXCEPTIONS;
770 class KWQPluginPart : public ReadOnlyPart
772 virtual bool openURL(const KURL &) { return true; }
773 virtual bool closeURL() { return true; }
776 ReadOnlyPart *KWQKHTMLPart::createPart(const ChildFrame &child, const KURL &url, const QString &mimeType)
778 KWQ_BLOCK_EXCEPTIONS;
781 BOOL needFrame = [_bridge frameRequiredForMIMEType:mimeType.getNSString() URL:url.getNSURL()];
782 if (child.m_type == ChildFrame::Object && !needFrame) {
783 KWQPluginPart *newPart = new KWQPluginPart;
784 newPart->setWidget(new QWidget([_bridge viewForPluginWithURL:url.getNSURL()
785 attributeNames:child.m_paramNames.getNSArray()
786 attributeValues:child.m_paramValues.getNSArray()
787 MIMEType:child.m_args.serviceType.getNSString()]));
790 LOG(Frames, "name %s", child.m_name.ascii());
791 BOOL allowsScrolling = YES;
792 int marginWidth = -1;
793 int marginHeight = -1;
794 if (child.m_type != ChildFrame::Object) {
795 HTMLFrameElementImpl *o = static_cast<HTMLFrameElementImpl *>(child.m_frame->element());
796 allowsScrolling = o->scrollingMode() != QScrollView::AlwaysOff;
797 marginWidth = o->getMarginWidth();
798 marginHeight = o->getMarginHeight();
800 WebCoreBridge *childBridge = [_bridge createChildFrameNamed:child.m_name.getNSString()
801 withURL:url.getNSURL()
802 renderPart:child.m_frame
803 allowsScrolling:allowsScrolling
804 marginWidth:marginWidth
805 marginHeight:marginHeight];
806 // This call needs to return an object with a ref, since the caller will expect to own it.
807 // childBridge owns the only ref so far.
808 [childBridge part]->ref();
809 part = [childBridge part];
814 KWQ_UNBLOCK_EXCEPTIONS;
819 void KWQKHTMLPart::setView(KHTMLView *view)
821 // Detach the document now, so any onUnload handlers get run - if
822 // we wait until the view is destroyed, then things won't be
823 // hooked up enough for some JavaScript calls to work.
824 if (d->m_doc && view == NULL) {
837 // Only one form submission is allowed per view of a part.
838 // Since this part may be getting reused as a result of being
839 // pulled from the back/forward cache, reset this flag.
840 _submittedFormURL = KURL();
843 KHTMLView *KWQKHTMLPart::view() const
848 void KWQKHTMLPart::setTitle(const DOMString &title)
850 QString text = title.string();
851 text.replace(QChar('\\'), backslashAsCurrencySymbol());
853 KWQ_BLOCK_EXCEPTIONS;
854 [_bridge setTitle:text.getNSString()];
855 KWQ_UNBLOCK_EXCEPTIONS;
858 void KWQKHTMLPart::setStatusBarText(const QString &status)
860 QString text = status;
861 text.replace(QChar('\\'), backslashAsCurrencySymbol());
863 KWQ_BLOCK_EXCEPTIONS;
864 [_bridge setStatusText:text.getNSString()];
865 KWQ_UNBLOCK_EXCEPTIONS;
868 void KWQKHTMLPart::scheduleClose()
870 KWQ_BLOCK_EXCEPTIONS;
871 [_bridge closeWindowSoon];
872 KWQ_UNBLOCK_EXCEPTIONS;
875 void KWQKHTMLPart::unfocusWindow()
877 KWQ_BLOCK_EXCEPTIONS;
878 [_bridge unfocusWindow];
879 KWQ_UNBLOCK_EXCEPTIONS;
882 void KWQKHTMLPart::jumpToSelection()
884 // Assumes that selection start will only ever be a text node. This is currently
885 // true, but will it always be so?
886 if (d->m_selection.start().isNotNull()) {
887 RenderText *rt = dynamic_cast<RenderText *>(d->m_selection.start().node()->renderer());
890 rt->posOfChar(d->m_selection.start().offset(), x, y);
891 // The -50 offset is copied from KHTMLPart::findTextNext, which sets the contents position
892 // after finding a matched text string.
893 d->m_view->setContentsPos(x - 50, y - 50);
896 Something like this would fix <rdar://problem/3154293>: "Find Next should not scroll page if the next target is already visible"
898 I think this would be a better way to do this, to avoid needless horizontal scrolling,
899 but it is not feasible until selectionRect() returns a tighter rect around the
900 selected text. Right now it works at element granularity.
902 NSView *docView = d->m_view->getDocumentView();
904 KWQ_BLOCK_EXCEPTIONS;
905 NSRect selRect = NSRect(selectionRect());
906 NSRect visRect = [docView visibleRect];
907 if (!NSContainsRect(visRect, selRect)) {
908 // pad a bit so we overscroll slightly
909 selRect = NSInsetRect(selRect, -10.0, -10.0);
910 selRect = NSIntersectionRect(selRect, [docView bounds]);
911 [docView scrollRectToVisible:selRect];
913 KWQ_UNBLOCK_EXCEPTIONS;
918 QString KWQKHTMLPart::advanceToNextMisspelling(bool startBeforeSelection)
920 // The basic approach is to search in two phases - from the selection end to the end of the doc, and
921 // then we wrap and search from the doc start to (approximately) where we started.
923 // Start at the end of the selection, search to edge of document. Starting at the selection end makes
924 // repeated "check spelling" commands work.
925 Range searchRange(xmlDocImpl());
926 searchRange.selectNodeContents(xmlDocImpl());
927 bool startedWithSelection = false;
928 if (selectionStart()) {
929 startedWithSelection = true;
930 if (startBeforeSelection) {
931 VisiblePosition start(selection().start());
932 // We match AppKit's rule: Start 1 character before the selection.
933 VisiblePosition oneBeforeStart = start.previous();
934 setStart(searchRange, oneBeforeStart.isNotNull() ? oneBeforeStart : start);
936 setStart(searchRange, VisiblePosition(selection().end()));
940 // If we're not in an editable node, try to find one, make that our range to work in
941 NodeImpl *editableNodeImpl = searchRange.startContainer().handle();
942 if (!editableNodeImpl->isContentEditable()) {
943 editableNodeImpl = editableNodeImpl->nextEditable();
944 if (!editableNodeImpl) {
947 searchRange.setStartBefore(editableNodeImpl);
948 startedWithSelection = false; // won't need to wrap
951 // topNode defines the whole range we want to operate on
952 Node topNode(editableNodeImpl->rootEditableElement());
953 searchRange.setEndAfter(topNode);
955 // Make sure start of searchRange is not in the middle of a word. Jumping back a char and then
956 // forward by a word happens to do the trick.
957 if (startedWithSelection) {
958 VisiblePosition oneBeforeStart = startVisiblePosition(searchRange).previous();
959 if (oneBeforeStart.isNotNull()) {
960 setStart(searchRange, endOfWord(oneBeforeStart));
961 } // else we were already at the start of the editable node
964 if (searchRange.collapsed()) {
965 return QString(); // nothing to search in
968 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
969 WordAwareIterator it(searchRange);
970 bool wrapped = false;
972 // We go to the end of our first range instead of the start of it, just to be sure
973 // we don't get foiled by any word boundary problems at the start. It means we might
974 // do a tiny bit more searching.
975 Node searchEndAfterWrapNode = it.range().endContainer();
976 long searchEndAfterWrapOffset = it.range().endOffset();
979 if (!it.atEnd()) { // we may be starting at the end of the doc, and already by atEnd
980 const QChar *chars = it.characters();
981 long len = it.length();
982 if (len > 1 || !chars[0].isSpace()) {
983 NSString *chunk = [[NSString alloc] initWithCharactersNoCopy:(unichar *)chars length:len freeWhenDone:NO];
984 NSRange misspelling = [checker checkSpellingOfString:chunk startingAt:0 language:nil wrap:NO inSpellDocumentWithTag:[_bridge spellCheckerDocumentTag] wordCount:NULL];
986 if (misspelling.length > 0) {
987 // Build up result range and string. Note the misspelling may span many text nodes,
988 // but the CharIterator insulates us from this complexity
989 Range misspellingRange(xmlDocImpl());
990 CharacterIterator chars(it.range());
991 chars.advance(misspelling.location);
992 misspellingRange.setStart(chars.range().startContainer(), chars.range().startOffset());
993 QString result = chars.string(misspelling.length);
994 misspellingRange.setEnd(chars.range().startContainer(), chars.range().startOffset());
996 setSelection(misspellingRange);
998 // Mark misspelling in document.
999 xmlDocImpl()->addMarker(misspellingRange, DocumentMarker::Spelling);
1007 if (wrapped || !startedWithSelection) {
1008 break; // finished the second range, or we did the whole doc with the first range
1010 // we've gone from the selection to the end of doc, now wrap around
1012 searchRange.setStartBefore(topNode);
1013 // going until the end of the very first chunk we tested is far enough
1014 searchRange.setEnd(searchEndAfterWrapNode, searchEndAfterWrapOffset);
1015 it = WordAwareIterator(searchRange);
1023 bool KWQKHTMLPart::scrollOverflow(KWQScrollDirection direction, KWQScrollGranularity granularity)
1025 if (!xmlDocImpl()) {
1029 NodeImpl *node = xmlDocImpl()->focusNode();
1031 node = d->m_mousePressNode.handle();
1035 RenderObject *r = node->renderer();
1037 return r->scroll(direction, granularity);
1044 bool KWQKHTMLPart::scrollOverflowWithScrollWheelEvent(NSEvent *event)
1046 RenderObject *r = renderer();
1051 NSPoint point = [d->m_view->getDocumentView() convertPoint:[event locationInWindow] fromView:nil];
1052 RenderObject::NodeInfo nodeInfo(true, true);
1053 r->layer()->hitTest(nodeInfo, (int)point.x, (int)point.y);
1055 NodeImpl *node = nodeInfo.innerNode();
1060 r = node->renderer();
1065 KWQScrollDirection direction;
1067 float deltaX = [event deltaX];
1068 float deltaY = [event deltaY];
1070 direction = KWQScrollRight;
1071 multiplier = -deltaX;
1072 } else if (deltaX > 0) {
1073 direction = KWQScrollLeft;
1074 multiplier = deltaX;
1075 } else if (deltaY < 0) {
1076 direction = KWQScrollDown;
1077 multiplier = -deltaY;
1078 } else if (deltaY > 0) {
1079 direction = KWQScrollUp;
1080 multiplier = deltaY;
1084 return r->scroll(direction, KWQScrollWheel, multiplier);
1087 void KWQKHTMLPart::redirectionTimerStartedOrStopped()
1089 // Don't report history navigations, just actual redirection.
1090 if (d->m_scheduledRedirection == historyNavigationScheduled) {
1094 KWQ_BLOCK_EXCEPTIONS;
1095 if (d->m_redirectionTimer.isActive()) {
1096 [_bridge reportClientRedirectToURL:KURL(d->m_redirectURL).getNSURL()
1097 delay:d->m_delayRedirect
1098 fireDate:[d->m_redirectionTimer.getNSTimer() fireDate]
1099 lockHistory:d->m_redirectLockHistory
1100 isJavaScriptFormAction:d->m_executingJavaScriptFormAction];
1102 [_bridge reportClientRedirectCancelled:d->m_cancelWithLoadInProgress];
1104 KWQ_UNBLOCK_EXCEPTIONS;
1107 void KWQKHTMLPart::paint(QPainter *p, const QRect &rect)
1111 if (p->device()->devType() == QInternal::Printer)
1112 fillWithRed = false; // Printing, don't fill with red (can't remember why).
1113 else if (!xmlDocImpl() || xmlDocImpl()->ownerElement())
1114 fillWithRed = false; // Subframe, don't fill with red.
1115 else if (view() && view()->isTransparent())
1116 fillWithRed = false; // Transparent, don't fill with red.
1117 else if (_drawSelectionOnly)
1118 fillWithRed = false; // Selections are transparent, don't fill with red.
1119 else if (_elementToDraw != 0)
1120 fillWithRed = false; // Element images are transparent, don't fill with red.
1125 p->fillRect(rect.x(), rect.y(), rect.width(), rect.height(), QColor(0xFF, 0, 0));
1130 // _elementToDraw is used to draw only one element
1131 RenderObject *eltRenderer = (_elementToDraw != 0) ? _elementToDraw.handle()->renderer() : 0;
1132 renderer()->layer()->paint(p, rect, _drawSelectionOnly, eltRenderer);
1135 // Regions may have changed as a result of the visibility/z-index of element changing.
1136 if (renderer()->document()->dashboardRegionsDirty()){
1137 renderer()->canvas()->view()->updateDashboardRegions();
1141 ERROR("called KWQKHTMLPart::paint with nil renderer");
1145 void KWQKHTMLPart::adjustPageHeight(float *newBottom, float oldTop, float oldBottom, float bottomLimit)
1147 RenderCanvas *root = static_cast<RenderCanvas *>(xmlDocImpl()->renderer());
1149 // Use a printer device, with painting disabled for the pagination phase
1150 QPainter painter(true);
1151 painter.setPaintingDisabled(true);
1153 root->setTruncatedAt((int)floor(oldBottom));
1154 QRect dirtyRect(0, (int)floor(oldTop),
1155 root->docWidth(), (int)ceil(oldBottom-oldTop));
1156 root->layer()->paint(&painter, dirtyRect);
1157 *newBottom = root->bestTruncatedAt();
1158 if (*newBottom == 0) {
1159 *newBottom = oldBottom;
1162 *newBottom = oldBottom;
1166 RenderObject *KWQKHTMLPart::renderer() const
1168 DocumentImpl *doc = xmlDocImpl();
1169 return doc ? doc->renderer() : 0;
1172 QString KWQKHTMLPart::userAgent() const
1174 KWQ_BLOCK_EXCEPTIONS;
1175 return QString::fromNSString([_bridge userAgentForURL:m_url.getNSURL()]);
1176 KWQ_UNBLOCK_EXCEPTIONS;
1181 QString KWQKHTMLPart::mimeTypeForFileName(const QString &fileName) const
1183 NSString *ns = fileName.getNSString();
1185 KWQ_BLOCK_EXCEPTIONS;
1186 return QString::fromNSString([_bridge MIMETypeForPath:ns]);
1187 KWQ_UNBLOCK_EXCEPTIONS;
1192 NSView *KWQKHTMLPart::nextKeyViewInFrame(NodeImpl *node, KWQSelectionDirection direction)
1194 DocumentImpl *doc = xmlDocImpl();
1199 node = direction == KWQSelectingNext
1200 ? doc->nextFocusNode(node) : doc->previousFocusNode(node);
1204 RenderWidget *renderWidget = dynamic_cast<RenderWidget *>(node->renderer());
1206 QWidget *widget = renderWidget->widget();
1207 KHTMLView *childFrameWidget = dynamic_cast<KHTMLView *>(widget);
1209 if (childFrameWidget) {
1210 view = KWQ(childFrameWidget->part())->nextKeyViewInFrame(0, direction);
1211 } else if (widget) {
1212 view = widget->getView();
1219 doc->setFocusNode(node);
1220 if (view() && node->renderer() && !node->renderer()->isRoot()) {
1221 view()->ensureRectVisibleCentered(node->getRect());
1223 [_bridge makeFirstResponder:[_bridge documentView]];
1224 return [_bridge documentView];
1229 NSView *KWQKHTMLPart::nextKeyViewInFrameHierarchy(NodeImpl *node, KWQSelectionDirection direction)
1231 NSView *next = nextKeyViewInFrame(node, direction);
1233 KWQKHTMLPart *parent = KWQ(parentPart());
1235 next = parent->nextKeyView(parent->childFrame(this)->m_frame->element(), direction);
1239 // remove focus from currently focused node if we're giving focus to another view
1240 if (next && (next != [_bridge documentView])) {
1241 DocumentImpl *doc = xmlDocImpl();
1243 doc->setFocusNode(0);
1250 NSView *KWQKHTMLPart::nextKeyView(NodeImpl *node, KWQSelectionDirection direction)
1252 KWQ_BLOCK_EXCEPTIONS;
1254 NSView * next = nextKeyViewInFrameHierarchy(node, direction);
1259 // Look at views from the top level part up, looking for a next key view that we can use.
1261 next = direction == KWQSelectingNext
1262 ? [_bridge nextKeyViewOutsideWebFrameViews]
1263 : [_bridge previousKeyViewOutsideWebFrameViews];
1269 KWQ_UNBLOCK_EXCEPTIONS;
1271 // If all else fails, make a loop by starting from 0.
1272 return nextKeyViewInFrameHierarchy(0, direction);
1275 NSView *KWQKHTMLPart::nextKeyViewForWidget(QWidget *startingWidget, KWQSelectionDirection direction)
1277 // Use the event filter object to figure out which RenderWidget owns this QWidget and get to the DOM.
1278 // Then get the next key view in the order determined by the DOM.
1279 NodeImpl *node = nodeForWidget(startingWidget);
1281 return partForNode(node)->nextKeyView(node, direction);
1284 bool KWQKHTMLPart::currentEventIsMouseDownInWidget(QWidget *candidate)
1286 KWQ_BLOCK_EXCEPTIONS;
1287 switch ([[NSApp currentEvent] type]) {
1288 case NSLeftMouseDown:
1289 case NSRightMouseDown:
1290 case NSOtherMouseDown:
1295 KWQ_UNBLOCK_EXCEPTIONS;
1297 NodeImpl *node = nodeForWidget(candidate);
1299 return partForNode(node)->nodeUnderMouse() == node;
1302 bool KWQKHTMLPart::currentEventIsKeyboardOptionTab()
1304 KWQ_BLOCK_EXCEPTIONS;
1305 NSEvent *evt = [NSApp currentEvent];
1306 if ([evt type] != NSKeyDown) {
1310 if (([evt modifierFlags] & NSAlternateKeyMask) == 0) {
1314 NSString *chars = [evt charactersIgnoringModifiers];
1315 if ([chars length] != 1)
1318 const unichar tabKey = 0x0009;
1319 const unichar shiftTabKey = 0x0019;
1320 unichar c = [chars characterAtIndex:0];
1321 if (c != tabKey && c != shiftTabKey)
1324 KWQ_UNBLOCK_EXCEPTIONS;
1328 bool KWQKHTMLPart::handleKeyboardOptionTabInView(NSView *view)
1330 if (KWQKHTMLPart::currentEventIsKeyboardOptionTab()) {
1331 if (([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) != 0) {
1332 [[view window] selectKeyViewPrecedingView:view];
1334 [[view window] selectKeyViewFollowingView:view];
1342 bool KWQKHTMLPart::tabsToLinks() const
1344 if ([_bridge keyboardUIMode] & WebCoreKeyboardAccessTabsToLinks)
1345 return !KWQKHTMLPart::currentEventIsKeyboardOptionTab();
1347 return KWQKHTMLPart::currentEventIsKeyboardOptionTab();
1350 bool KWQKHTMLPart::tabsToAllControls() const
1352 WebCoreKeyboardUIMode keyboardUIMode = [_bridge keyboardUIMode];
1353 BOOL handlingOptionTab = KWQKHTMLPart::currentEventIsKeyboardOptionTab();
1355 // If tab-to-links is off, option-tab always highlights all controls
1356 if ((keyboardUIMode & WebCoreKeyboardAccessTabsToLinks) == 0 && handlingOptionTab) {
1360 // If system preferences say to include all controls, we always include all controls
1361 if (keyboardUIMode & WebCoreKeyboardAccessFull) {
1365 // Otherwise tab-to-links includes all controls, unless the sense is flipped via option-tab.
1366 if (keyboardUIMode & WebCoreKeyboardAccessTabsToLinks) {
1367 return !handlingOptionTab;
1370 return handlingOptionTab;
1373 KJS::Bindings::RootObject *KWQKHTMLPart::executionContextForDOM()
1375 return bindingRootObject();
1378 KJS::Bindings::RootObject *KWQKHTMLPart::bindingRootObject()
1380 if (!_bindingRoot) {
1381 _bindingRoot = new KJS::Bindings::RootObject(0); // The root gets deleted by JavaScriptCore.
1382 KJS::ObjectImp *win = static_cast<KJS::ObjectImp *>(KJS::Window::retrieveWindow(this));
1383 _bindingRoot->setRootObjectImp (win);
1384 _bindingRoot->setInterpreter (KJSProxy::proxy(this)->interpreter());
1385 addPluginRootObject (_bindingRoot);
1387 return _bindingRoot;
1390 WebScriptObject *KWQKHTMLPart::windowScriptObject()
1392 if (!_windowScriptObject) {
1393 KJS::ObjectImp *win = static_cast<KJS::ObjectImp *>(KJS::Window::retrieveWindow(this));
1394 _windowScriptObject = KWQRetainNSRelease([[WebScriptObject alloc] _initWithObjectImp:win root:bindingRootObject()]);
1397 return _windowScriptObject;
1400 NPObject *KWQKHTMLPart::windowScriptNPObject()
1402 if (!_windowScriptNPObject) {
1403 KJS::ObjectImp *win = static_cast<KJS::ObjectImp *>(KJS::Window::retrieveWindow(this));
1404 _windowScriptNPObject = _NPN_CreateScriptObject (0, win, bindingRootObject());
1407 return _windowScriptNPObject;
1410 void KWQKHTMLPart::partClearedInBegin()
1412 [_bridge windowObjectCleared];
1415 QMap<int, ScheduledAction*> *KWQKHTMLPart::pauseActions(const void *key)
1417 if (d->m_doc && d->m_jscript) {
1418 Window *w = Window::retrieveWindow(this);
1419 if (w && w->hasTimeouts()) {
1420 return w->pauseTimeouts(key);
1426 void KWQKHTMLPart::resumeActions(QMap<int, ScheduledAction*> *actions, const void *key)
1428 if (d->m_doc && d->m_jscript && d->m_bJScriptEnabled) {
1429 Window *w = Window::retrieveWindow(this);
1431 w->resumeTimeouts(actions, key);
1436 bool KWQKHTMLPart::canCachePage()
1438 // Only save page state if:
1439 // 1. We're not a frame or frameset.
1440 // 2. The page has no unload handler.
1441 // 3. The page has no password fields.
1442 // 4. The URL for the page is not https.
1443 // 5. The page has no applets.
1444 if (d->m_frames.count() ||
1446 m_url.protocol().startsWith("https") ||
1447 (d->m_doc && (htmlDocument().applets().length() != 0 ||
1448 d->m_doc->hasWindowEventListener(EventImpl::UNLOAD_EVENT) ||
1449 d->m_doc->hasPasswordField()))) {
1455 void KWQKHTMLPart::saveWindowProperties(SavedProperties *windowProperties)
1457 Window *window = Window::retrieveWindow(this);
1459 window->saveProperties(*windowProperties);
1462 void KWQKHTMLPart::saveLocationProperties(SavedProperties *locationProperties)
1464 Window *window = Window::retrieveWindow(this);
1466 Interpreter::lock();
1467 Location *location = window->location();
1468 Interpreter::unlock();
1469 location->saveProperties(*locationProperties);
1473 void KWQKHTMLPart::restoreWindowProperties(SavedProperties *windowProperties)
1475 Window *window = Window::retrieveWindow(this);
1477 window->restoreProperties(*windowProperties);
1480 void KWQKHTMLPart::restoreLocationProperties(SavedProperties *locationProperties)
1482 Window *window = Window::retrieveWindow(this);
1484 Interpreter::lock();
1485 Location *location = window->location();
1486 Interpreter::unlock();
1487 location->restoreProperties(*locationProperties);
1491 void KWQKHTMLPart::saveInterpreterBuiltins(SavedBuiltins &interpreterBuiltins)
1493 if (jScript() && jScript()->interpreter()) {
1494 jScript()->interpreter()->saveBuiltins(interpreterBuiltins);
1498 void KWQKHTMLPart::restoreInterpreterBuiltins(const SavedBuiltins &interpreterBuiltins)
1500 if (jScript() && jScript()->interpreter()) {
1501 jScript()->interpreter()->restoreBuiltins(interpreterBuiltins);
1505 void KWQKHTMLPart::openURLFromPageCache(KWQPageState *state)
1507 // It's safe to assume none of the KWQPageState methods will raise
1508 // exceptions, since KWQPageState is implemented by WebCore and
1511 DocumentImpl *doc = [state document];
1512 KURL *url = [state URL];
1513 SavedProperties *windowProperties = [state windowProperties];
1514 SavedProperties *locationProperties = [state locationProperties];
1515 SavedBuiltins *interpreterBuiltins = [state interpreterBuiltins];
1516 QMap<int, ScheduledAction*> *actions = [state pausedActions];
1518 cancelRedirection();
1520 // We still have to close the previous part page.
1521 if (!d->m_restored){
1525 d->m_bComplete = false;
1527 // Don't re-emit the load event.
1528 d->m_bLoadEventEmitted = true;
1530 // delete old status bar msg's from kjs (if it _was_ activated on last URL)
1531 if( d->m_bJScriptEnabled )
1533 d->m_kjsStatusBarText = QString::null;
1534 d->m_kjsDefaultStatusBarText = QString::null;
1541 // 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
1542 // data arrives) (Simon)
1543 if(m_url.protocol().startsWith( "http" ) && !m_url.host().isEmpty() && m_url.path().isEmpty()) {
1545 emit d->m_extension->setLocationBarURL( m_url.prettyURL() );
1548 // copy to m_workingURL after fixing m_url above
1549 d->m_workingURL = m_url;
1553 // -----------begin-----------
1556 doc->setInPageCache(NO);
1558 d->m_bCleared = false;
1560 d->m_bComplete = false;
1561 d->m_bLoadEventEmitted = false;
1562 d->m_referrer = m_url.url();
1564 setView(doc->view());
1569 Decoder *decoder = doc->decoder();
1574 d->m_decoder->deref();
1576 d->m_decoder = decoder;
1578 updatePolicyBaseURL();
1580 restoreWindowProperties (windowProperties);
1581 restoreLocationProperties (locationProperties);
1582 restoreInterpreterBuiltins (*interpreterBuiltins);
1585 resumeActions (actions, state);
1590 KWQKHTMLPart *KWQKHTMLPart::partForWidget(const QWidget *widget)
1592 ASSERT_ARG(widget, widget);
1594 NodeImpl *node = nodeForWidget(widget);
1596 return partForNode(node);
1599 // Assume all widgets are either form controls, or KHTMLViews.
1600 const KHTMLView *view = dynamic_cast<const KHTMLView *>(widget);
1602 return KWQ(view->part());
1605 WebCoreBridge *KWQKHTMLPart::bridgeForWidget(const QWidget *widget)
1607 ASSERT_ARG(widget, widget);
1609 KWQKHTMLPart *part = partForWidget(widget);
1611 return part->bridge();
1614 KWQKHTMLPart *KWQKHTMLPart::partForNode(NodeImpl *node)
1616 ASSERT_ARG(node, node);
1617 return KWQ(node->getDocument()->part());
1620 NSView *KWQKHTMLPart::documentViewForNode(DOM::NodeImpl *node)
1622 WebCoreBridge *bridge = partForNode(node)->bridge();
1623 return [bridge documentView];
1626 NodeImpl *KWQKHTMLPart::nodeForWidget(const QWidget *widget)
1628 ASSERT_ARG(widget, widget);
1629 const QObject *o = widget->eventFilterObject();
1630 return o ? static_cast<const RenderWidget *>(o)->element() : 0;
1633 void KWQKHTMLPart::setDocumentFocus(QWidget *widget)
1635 NodeImpl *node = nodeForWidget(widget);
1637 node->getDocument()->setFocusNode(node);
1639 ERROR("unable to clear focus because widget had no corresponding node");
1643 void KWQKHTMLPart::clearDocumentFocus(QWidget *widget)
1645 NodeImpl *node = nodeForWidget(widget);
1647 node->getDocument()->setFocusNode(0);
1649 ERROR("unable to clear focus because widget had no corresponding node");
1653 void KWQKHTMLPart::saveDocumentState()
1655 // Do not save doc state if the page has a password field and a form that would be submitted
1657 if (!(d->m_doc && d->m_doc->hasPasswordField() && d->m_doc->hasSecureForm())) {
1658 KWQ_BLOCK_EXCEPTIONS;
1659 [_bridge saveDocumentState];
1660 KWQ_UNBLOCK_EXCEPTIONS;
1664 void KWQKHTMLPart::restoreDocumentState()
1666 KWQ_BLOCK_EXCEPTIONS;
1667 [_bridge restoreDocumentState];
1668 KWQ_UNBLOCK_EXCEPTIONS;
1671 QPtrList<KWQKHTMLPart> &KWQKHTMLPart::mutableInstances()
1673 static QPtrList<KWQKHTMLPart> instancesList;
1674 return instancesList;
1677 void KWQKHTMLPart::updatePolicyBaseURL()
1679 if (parentPart() && parentPart()->xmlDocImpl()) {
1680 setPolicyBaseURL(parentPart()->xmlDocImpl()->policyBaseURL());
1682 setPolicyBaseURL(m_url.url());
1686 void KWQKHTMLPart::setPolicyBaseURL(const DOMString &s)
1689 xmlDocImpl()->setPolicyBaseURL(s);
1690 ConstFrameIt end = d->m_frames.end();
1691 for (ConstFrameIt it = d->m_frames.begin(); it != end; ++it) {
1692 ReadOnlyPart *subpart = (*it).m_part;
1693 static_cast<KWQKHTMLPart *>(subpart)->setPolicyBaseURL(s);
1697 QString KWQKHTMLPart::requestedURLString() const
1699 KWQ_BLOCK_EXCEPTIONS;
1700 return QString::fromNSString([_bridge requestedURLString]);
1701 KWQ_UNBLOCK_EXCEPTIONS;
1706 QString KWQKHTMLPart::incomingReferrer() const
1708 KWQ_BLOCK_EXCEPTIONS;
1709 return QString::fromNSString([_bridge incomingReferrer]);
1710 KWQ_UNBLOCK_EXCEPTIONS;
1715 void KWQKHTMLPart::forceLayout()
1717 KHTMLView *v = d->m_view;
1720 // We cannot unschedule a pending relayout, since the force can be called with
1721 // a tiny rectangle from a drawRect update. By unscheduling we in effect
1722 // "validate" and stop the necessary full repaint from occurring. Basically any basic
1723 // append/remove DHTML is broken by this call. For now, I have removed the optimization
1724 // until we have a better invalidation stategy. -dwh
1725 //v->unscheduleRelayout();
1729 void KWQKHTMLPart::forceLayoutWithPageWidthRange(float minPageWidth, float maxPageWidth)
1731 // Dumping externalRepresentation(_part->renderer()).ascii() is a good trick to see
1732 // the state of things before and after the layout
1733 RenderCanvas *root = static_cast<RenderCanvas *>(xmlDocImpl()->renderer());
1735 // This magic is basically copied from khtmlview::print
1736 int pageW = (int)ceil(minPageWidth);
1737 root->setWidth(pageW);
1738 root->setNeedsLayoutAndMinMaxRecalc();
1741 // If we don't fit in the minimum page width, we'll lay out again. If we don't fit in the
1742 // maximum page width, we will lay out to the maximum page width and clip extra content.
1743 // FIXME: We are assuming a shrink-to-fit printing implementation. A cropping
1744 // implementation should not do this!
1745 int rightmostPos = root->rightmostPosition();
1746 if (rightmostPos > minPageWidth) {
1747 pageW = kMin(rightmostPos, (int)ceil(maxPageWidth));
1748 root->setWidth(pageW);
1749 root->setNeedsLayoutAndMinMaxRecalc();
1755 void KWQKHTMLPart::sendResizeEvent()
1757 KHTMLView *v = d->m_view;
1764 void KWQKHTMLPart::sendScrollEvent()
1766 KHTMLView *v = d->m_view;
1768 DocumentImpl *doc = xmlDocImpl();
1771 doc->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, true, false);
1775 void KWQKHTMLPart::runJavaScriptAlert(const QString &message)
1777 QString text = message;
1778 text.replace(QChar('\\'), backslashAsCurrencySymbol());
1779 KWQ_BLOCK_EXCEPTIONS;
1780 [_bridge runJavaScriptAlertPanelWithMessage:text.getNSString()];
1781 KWQ_UNBLOCK_EXCEPTIONS;
1784 bool KWQKHTMLPart::runJavaScriptConfirm(const QString &message)
1786 QString text = message;
1787 text.replace(QChar('\\'), backslashAsCurrencySymbol());
1789 KWQ_BLOCK_EXCEPTIONS;
1790 return [_bridge runJavaScriptConfirmPanelWithMessage:text.getNSString()];
1791 KWQ_UNBLOCK_EXCEPTIONS;
1796 bool KWQKHTMLPart::runJavaScriptPrompt(const QString &prompt, const QString &defaultValue, QString &result)
1798 QString promptText = prompt;
1799 promptText.replace(QChar('\\'), backslashAsCurrencySymbol());
1800 QString defaultValueText = defaultValue;
1801 defaultValueText.replace(QChar('\\'), backslashAsCurrencySymbol());
1803 KWQ_BLOCK_EXCEPTIONS;
1804 NSString *returnedText = nil;
1806 bool ok = [_bridge runJavaScriptTextInputPanelWithPrompt:prompt.getNSString()
1807 defaultText:defaultValue.getNSString() returningText:&returnedText];
1810 result = QString::fromNSString(returnedText);
1811 result.replace(backslashAsCurrencySymbol(), QChar('\\'));
1815 KWQ_UNBLOCK_EXCEPTIONS;
1820 bool KWQKHTMLPart::locationbarVisible()
1822 return [_bridge areToolbarsVisible];
1825 bool KWQKHTMLPart::menubarVisible()
1827 // The menubar is always on in Mac OS X UI
1831 bool KWQKHTMLPart::personalbarVisible()
1833 return [_bridge areToolbarsVisible];
1836 bool KWQKHTMLPart::scrollbarsVisible()
1841 if (view()->hScrollBarMode() == QScrollView::AlwaysOff || view()->vScrollBarMode() == QScrollView::AlwaysOff)
1847 bool KWQKHTMLPart::statusbarVisible()
1849 return [_bridge isStatusBarVisible];
1852 bool KWQKHTMLPart::toolbarVisible()
1854 return [_bridge areToolbarsVisible];
1857 void KWQKHTMLPart::addMessageToConsole(const QString &message, unsigned lineNumber, const QString &sourceURL)
1859 NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
1860 message.getNSString(), @"message",
1861 [NSNumber numberWithInt: lineNumber], @"lineNumber",
1862 sourceURL.getNSString(), @"sourceURL",
1864 [_bridge addMessageToConsole:dictionary];
1867 void KWQKHTMLPart::createEmptyDocument()
1869 // Although it's not completely clear from the name of this function,
1870 // it does nothing if we already have a document, and just creates an
1871 // empty one if we have no document at all.
1873 KWQ_BLOCK_EXCEPTIONS;
1874 [_bridge loadEmptyDocumentSynchronously];
1875 KWQ_UNBLOCK_EXCEPTIONS;
1877 if (parentPart() && (parentPart()->childFrame(this)->m_type == ChildFrame::IFrame ||
1878 parentPart()->childFrame(this)->m_type == ChildFrame::Object)) {
1879 d->m_doc->setBaseURL(parentPart()->d->m_doc->baseURL());
1884 void KWQKHTMLPart::addMetaData(const QString &key, const QString &value)
1886 d->m_job->addMetaData(key, value);
1889 bool KWQKHTMLPart::keyEvent(NSEvent *event)
1891 KWQ_BLOCK_EXCEPTIONS;
1893 ASSERT([event type] == NSKeyDown || [event type] == NSKeyUp);
1895 // Check for cases where we are too early for events -- possible unmatched key up
1896 // from pressing return in the location bar.
1897 DocumentImpl *doc = xmlDocImpl();
1901 NodeImpl *node = doc->focusNode();
1909 if ([event type] == NSKeyDown) {
1910 prepareForUserAction();
1913 NSEvent *oldCurrentEvent = _currentEvent;
1914 _currentEvent = KWQRetain(event);
1916 QKeyEvent qEvent(event);
1917 bool result = !node->dispatchKeyEvent(&qEvent);
1919 // We want to send both a down and a press for the initial key event.
1920 // To get KHTML to do this, we send a second KeyPress QKeyEvent with "is repeat" set to true,
1921 // which causes it to send a press to the DOM.
1922 // That's not a great hack; it would be good to do this in a better way.
1923 if ([event type] == NSKeyDown && ![event isARepeat]) {
1924 QKeyEvent repeatEvent(event, true);
1925 if (!node->dispatchKeyEvent(&repeatEvent)) {
1930 ASSERT(_currentEvent == event);
1932 _currentEvent = oldCurrentEvent;
1936 KWQ_UNBLOCK_EXCEPTIONS;
1941 // This does the same kind of work that KHTMLPart::openURL does, except it relies on the fact
1942 // that a higher level already checked that the URLs match and the scrolling is the right thing to do.
1943 void KWQKHTMLPart::scrollToAnchor(const KURL &URL)
1950 // It's important to model this as a load that starts and immediately finishes.
1951 // Otherwise, the parent frame may think we never finished loading.
1952 d->m_bComplete = false;
1956 bool KWQKHTMLPart::closeURL()
1958 saveDocumentState();
1959 return KHTMLPart::closeURL();
1962 void KWQKHTMLPart::khtmlMousePressEvent(MousePressEvent *event)
1964 bool singleClick = [_currentEvent clickCount] <= 1;
1966 // If we got the event back, that must mean it wasn't prevented,
1967 // so it's allowed to start a drag or selection.
1968 _mouseDownMayStartSelect = true;
1969 // Careful that the drag starting logic stays in sync with eventMayStartDrag()
1970 _mouseDownMayStartDrag = singleClick;
1972 d->m_mousePressNode = event->innerNode();
1974 if (!passWidgetMouseDownEventToWidget(event)) {
1975 // We don't do this at the start of mouse down handling (before calling into WebCore),
1976 // because we don't want to do it until we know we didn't hit a widget.
1977 NSView *view = d->m_view->getDocumentView();
1980 KWQ_BLOCK_EXCEPTIONS;
1981 if ([_bridge firstResponder] != view) {
1982 [_bridge makeFirstResponder:view];
1984 KWQ_UNBLOCK_EXCEPTIONS;
1987 KHTMLPart::khtmlMousePressEvent(event);
1991 void KWQKHTMLPart::khtmlMouseDoubleClickEvent(MouseDoubleClickEvent *event)
1993 if (!passWidgetMouseDownEventToWidget(event)) {
1994 KHTMLPart::khtmlMouseDoubleClickEvent(event);
1998 bool KWQKHTMLPart::passWidgetMouseDownEventToWidget(khtml::MouseEvent *event)
2000 // Figure out which view to send the event to.
2001 RenderObject *target = event->innerNode().handle() ? event->innerNode().handle()->renderer() : 0;
2005 QWidget* widget = RenderLayer::gScrollBar;
2007 if (!target->isWidget())
2009 widget = static_cast<RenderWidget *>(target)->widget();
2012 // Doubleclick events don't exist in Cocoa. Since passWidgetMouseDownEventToWidget will
2013 // just pass _currentEvent down to the widget, we don't want to call it for events that
2014 // don't correspond to Cocoa events. The mousedown/ups will have already been passed on as
2015 // part of the pressed/released handling.
2016 if (!MouseDoubleClickEvent::test(event))
2017 return passWidgetMouseDownEventToWidget(widget);
2022 bool KWQKHTMLPart::passWidgetMouseDownEventToWidget(RenderWidget *renderWidget)
2024 return passWidgetMouseDownEventToWidget(renderWidget->widget());
2027 bool KWQKHTMLPart::passWidgetMouseDownEventToWidget(QWidget* widget)
2029 // FIXME: this method always returns true
2032 ERROR("hit a RenderWidget without a corresponding QWidget, means a frame is half-constructed");
2036 KWQ_BLOCK_EXCEPTIONS;
2038 NSView *nodeView = widget->getView();
2040 ASSERT([nodeView superview]);
2041 NSView *view = [nodeView hitTest:[[nodeView superview] convertPoint:[_currentEvent locationInWindow] fromView:nil]];
2043 ERROR("KHTML says we hit a RenderWidget, but AppKit doesn't agree we hit the corresponding NSView");
2047 if ([_bridge firstResponder] == view) {
2048 // In the case where we just became first responder, we should send the mouseDown:
2049 // to the NSTextField, not the NSTextField's editor. This code makes sure that happens.
2050 // If we don't do this, we see a flash of selected text when clicking in a text field.
2051 if (![_bridge wasFirstResponderAtMouseDownTime:view] && [view isKindOfClass:[NSTextView class]]) {
2052 NSView *superview = view;
2053 while (superview != nodeView) {
2054 superview = [superview superview];
2056 if ([superview isKindOfClass:[NSControl class]]) {
2057 NSControl *control = superview;
2058 if ([control currentEditor] == view) {
2066 // Normally [NSWindow sendEvent:] handles setting the first responder.
2067 // But in our case, the event was sent to the view representing the entire web page.
2068 if ([_currentEvent clickCount] <= 1 && [view acceptsFirstResponder] && [view needsPanelToBecomeKey]) {
2069 [_bridge makeFirstResponder:view];
2073 // We need to "defer loading" and defer timers while we are tracking the mouse.
2074 // That's because we don't want the new page to load while the user is holding the mouse down.
2076 BOOL wasDeferringLoading = [_bridge defersLoading];
2077 if (!wasDeferringLoading) {
2078 [_bridge setDefersLoading:YES];
2080 BOOL wasDeferringTimers = QObject::defersTimers();
2081 if (!wasDeferringTimers) {
2082 QObject::setDefersTimers(true);
2085 ASSERT(!_sendingEventToSubview);
2086 _sendingEventToSubview = true;
2087 [view mouseDown:_currentEvent];
2088 _sendingEventToSubview = false;
2090 if (!wasDeferringTimers) {
2091 QObject::setDefersTimers(false);
2093 if (!wasDeferringLoading) {
2094 [_bridge setDefersLoading:NO];
2097 // Remember which view we sent the event to, so we can direct the release event properly.
2098 _mouseDownView = view;
2099 _mouseDownWasInSubframe = false;
2101 KWQ_UNBLOCK_EXCEPTIONS;
2106 bool KWQKHTMLPart::lastEventIsMouseUp() const
2108 // Many AK widgets run their own event loops and consume events while the mouse is down.
2109 // When they finish, currentEvent is the mouseUp that they exited on. We need to update
2110 // the khtml state with this mouseUp, which khtml never saw. This method lets us detect
2113 KWQ_BLOCK_EXCEPTIONS;
2114 NSEvent *currentEventAfterHandlingMouseDown = [NSApp currentEvent];
2115 if (_currentEvent != currentEventAfterHandlingMouseDown) {
2116 if ([currentEventAfterHandlingMouseDown type] == NSLeftMouseUp) {
2120 KWQ_UNBLOCK_EXCEPTIONS;
2125 // Note that this does the same kind of check as [target isDescendantOf:superview].
2126 // There are two differences: This is a lot slower because it has to walk the whole
2127 // tree, and this works in cases where the target has already been deallocated.
2128 static bool findViewInSubviews(NSView *superview, NSView *target)
2130 KWQ_BLOCK_EXCEPTIONS;
2131 NSEnumerator *e = [[superview subviews] objectEnumerator];
2133 while ((subview = [e nextObject])) {
2134 if (subview == target || findViewInSubviews(subview, target)) {
2138 KWQ_UNBLOCK_EXCEPTIONS;
2143 NSView *KWQKHTMLPart::mouseDownViewIfStillGood()
2145 // Since we have no way of tracking the lifetime of _mouseDownView, we have to assume that
2146 // it could be deallocated already. We search for it in our subview tree; if we don't find
2147 // it, we set it to nil.
2148 NSView *mouseDownView = _mouseDownView;
2149 if (!mouseDownView) {
2152 KHTMLView *topKHTMLView = d->m_view;
2153 NSView *topView = topKHTMLView ? topKHTMLView->getView() : nil;
2154 if (!topView || !findViewInSubviews(topView, mouseDownView)) {
2155 _mouseDownView = nil;
2158 return mouseDownView;
2161 // The link drag hysteresis is much larger than the others because there
2162 // needs to be enough space to cancel the link press without starting a link drag,
2163 // and because dragging links is rare.
2164 #define LinkDragHysteresis 40.0
2165 #define ImageDragHysteresis 5.0
2166 #define TextDragHysteresis 3.0
2167 #define GeneralDragHysteresis 3.0
2169 #define TextDragDelay 0.15
2171 bool KWQKHTMLPart::dragHysteresisExceeded(float dragLocationX, float dragLocationY) const
2174 d->m_view->viewportToContents((int)dragLocationX, (int)dragLocationY, dragX, dragY);
2175 float deltaX = QABS(dragX - _mouseDownX);
2176 float deltaY = QABS(dragY - _mouseDownY);
2178 float threshold = GeneralDragHysteresis;
2179 if (_dragSrcIsImage) {
2180 threshold = ImageDragHysteresis;
2181 } else if (_dragSrcIsLink) {
2182 threshold = LinkDragHysteresis;
2183 } else if (_dragSrcInSelection) {
2184 threshold = TextDragHysteresis;
2186 return deltaX >= threshold || deltaY >= threshold;
2189 // returns if we should continue "default processing", i.e., whether eventhandler canceled
2190 bool KWQKHTMLPart::dispatchDragSrcEvent(int eventId, const QPoint &loc) const
2192 bool noDefaultProc = d->m_view->dispatchDragEvent(eventId, _dragSrc.handle(), loc, _dragClipboard);
2193 return !noDefaultProc;
2196 bool KWQKHTMLPart::eventMayStartDrag(NSEvent *event) const
2198 // This is a pre-flight check of whether the event might lead to a drag being started. Be careful
2199 // that its logic needs to stay in sync with khtmlMouseMoveEvent() and the way we set
2200 // _mouseDownMayStartDrag in khtmlMousePressEvent
2202 if ([event type] != NSLeftMouseDown || [event clickCount] != 1) {
2206 BOOL DHTMLFlag, UAFlag;
2207 [_bridge allowDHTMLDrag:&DHTMLFlag UADrag:&UAFlag];
2208 if (!DHTMLFlag && !UAFlag) {
2212 NSPoint loc = [event locationInWindow];
2213 int mouseDownX, mouseDownY;
2214 d->m_view->viewportToContents((int)loc.x, (int)loc.y, mouseDownX, mouseDownY);
2215 RenderObject::NodeInfo nodeInfo(true, false);
2216 renderer()->layer()->hitTest(nodeInfo, mouseDownX, mouseDownY);
2218 Node possibleSrc = nodeInfo.innerNode()->renderer()->draggableNode(DHTMLFlag, UAFlag, mouseDownX, mouseDownY, srcIsDHTML);
2219 return !possibleSrc.isNull();
2222 void KWQKHTMLPart::khtmlMouseMoveEvent(MouseMoveEvent *event)
2224 KWQ_BLOCK_EXCEPTIONS;
2226 if ([_currentEvent type] == NSLeftMouseDragged) {
2227 NSView *view = mouseDownViewIfStillGood();
2230 _sendingEventToSubview = true;
2231 [view mouseDragged:_currentEvent];
2232 _sendingEventToSubview = false;
2236 // Careful that the drag starting logic stays in sync with eventMayStartDrag()
2238 if (_mouseDownMayStartDrag && _dragSrc.isNull()) {
2239 BOOL tempFlag1, tempFlag2;
2240 [_bridge allowDHTMLDrag:&tempFlag1 UADrag:&tempFlag2];
2241 _dragSrcMayBeDHTML = tempFlag1;
2242 _dragSrcMayBeUA = tempFlag2;
2243 if (!_dragSrcMayBeDHTML && !_dragSrcMayBeUA) {
2244 _mouseDownMayStartDrag = false; // no element is draggable
2248 if (_mouseDownMayStartDrag && _dragSrc.isNull()) {
2249 // try to find an element that wants to be dragged
2250 RenderObject::NodeInfo nodeInfo(true, false);
2251 renderer()->layer()->hitTest(nodeInfo, _mouseDownX, _mouseDownY);
2252 _dragSrc = nodeInfo.innerNode()->renderer()->draggableNode(_dragSrcMayBeDHTML, _dragSrcMayBeUA, _mouseDownX, _mouseDownY, _dragSrcIsDHTML);
2253 if (_dragSrc.isNull()) {
2254 _mouseDownMayStartDrag = false; // no element is draggable
2256 // remember some facts about this source, while we have a NodeInfo handy
2257 NodeImpl *node = nodeInfo.URLElement();
2258 _dragSrcIsLink = node ? node->hasAnchor() : false;
2260 node = nodeInfo.innerNonSharedNode();
2261 _dragSrcIsImage = (node && node->renderer() && node->renderer()->isImage());
2263 _dragSrcInSelection = isPointInsideSelection(_mouseDownX, _mouseDownY);
2267 // For drags starting in the selection, the user must wait between the mousedown and mousedrag,
2268 // or else we bail on the dragging stuff and allow selection to occur
2269 if (_mouseDownMayStartDrag && _dragSrcInSelection && [_currentEvent timestamp] - _mouseDownTimestamp < TextDragDelay) {
2270 _mouseDownMayStartDrag = false;
2271 // ...but if this was the first click in the window, we don't even want to start selection
2272 if (_activationEventNumber == [_currentEvent eventNumber]) {
2273 _mouseDownMayStartSelect = false;
2277 if (_mouseDownMayStartDrag) {
2278 // We are starting a text/image/url drag, so the cursor should be an arrow
2279 d->m_view->resetCursor();
2281 NSPoint dragLocation = [_currentEvent locationInWindow];
2282 if (dragHysteresisExceeded(dragLocation.x, dragLocation.y)) {
2284 // Once we're past the hysteresis point, we don't want to treat this gesture as a click
2285 d->m_view->invalidateClick();
2287 NSImage *dragImage = nil; // we use these values if WC is out of the loop
2288 NSPoint dragLoc = NSZeroPoint;
2289 NSDragOperation srcOp = NSDragOperationNone;
2290 BOOL wcWrotePasteboard = NO;
2291 if (_dragSrcMayBeDHTML) {
2292 NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard];
2293 // Must be done before ondragstart adds types and data to the pboard,
2294 // also done for security, as it erases data from the last drag
2295 [pasteboard declareTypes:[NSArray array] owner:nil];
2297 freeClipboard(); // would only happen if we missed a dragEnd. Do it anyway, just
2298 // to make sure it gets numbified
2299 _dragClipboard = new KWQClipboard(true, pasteboard, KWQClipboard::Writable, this);
2300 _dragClipboard->ref();
2302 // If this is drag of an element, get set up to generate a default image. Otherwise
2303 // WebKit will generate the default, the element doesn't override.
2304 if (_dragSrcIsDHTML) {
2306 _dragSrc.handle()->renderer()->absolutePosition(srcX, srcY);
2307 _dragClipboard->setDragImageElement(_dragSrc, QPoint(_mouseDownX - srcX, _mouseDownY - srcY));
2310 _mouseDownMayStartDrag = dispatchDragSrcEvent(EventImpl::DRAGSTART_EVENT, QPoint(_mouseDownWinX, _mouseDownWinY));
2311 // Invalidate clipboard here against anymore pasteboard writing for security. The drag
2312 // image can still be changed as we drag, but not the pasteboard data.
2313 _dragClipboard->setAccessPolicy(KWQClipboard::ImageWritable);
2315 if (_mouseDownMayStartDrag) {
2316 // gather values from DHTML element, if it set any
2317 _dragClipboard->sourceOperation(&srcOp);
2319 NSArray *types = [pasteboard types];
2320 wcWrotePasteboard = types && [types count] > 0;
2322 if (_dragSrcMayBeDHTML) {
2323 dragImage = _dragClipboard->dragNSImage(&dragLoc);
2326 // Yuck, dragSourceMovedTo() can be called as a result of kicking off the drag with
2327 // dragImage! Because of that dumb reentrancy, we may think we've not started the
2328 // drag when that happens. So we have to assume it's started before we kick it off.
2329 _dragClipboard->setDragHasStarted();
2333 if (_mouseDownMayStartDrag) {
2334 BOOL startedDrag = [_bridge startDraggingImage:dragImage at:dragLoc operation:srcOp event:_currentEvent sourceIsDHTML:_dragSrcIsDHTML DHTMLWroteData:wcWrotePasteboard];
2335 if (!startedDrag && _dragSrcMayBeDHTML) {
2336 // WebKit canned the drag at the last minute - we owe _dragSrc a DRAGEND event
2337 dispatchDragSrcEvent(EventImpl::DRAGEND_EVENT, QPoint(dragLocation));
2338 _mouseDownMayStartDrag = false;
2342 if (!_mouseDownMayStartDrag) {
2343 // something failed to start the drag, cleanup
2349 // No more default handling (like selection), whether we're past the hysteresis bounds or not
2352 if (!_mouseDownMayStartSelect) {
2356 // Don't allow dragging or click handling after we've started selecting.
2357 _mouseDownMayStartDrag = false;
2358 d->m_view->invalidateClick();
2360 // We use khtml's selection but our own autoscrolling.
2361 [_bridge handleAutoscrollForMouseDragged:_currentEvent];
2363 // If we allowed the other side of the bridge to handle a drag
2364 // last time, then m_bMousePressed might still be set. So we
2365 // clear it now to make sure the next move after a drag
2366 // doesn't look like a drag.
2367 d->m_bMousePressed = false;
2370 KHTMLPart::khtmlMouseMoveEvent(event);
2372 KWQ_UNBLOCK_EXCEPTIONS;
2375 void KWQKHTMLPart::dragSourceMovedTo(const QPoint &loc)
2377 if (!_dragSrc.isNull() && _dragSrcMayBeDHTML) {
2378 // for now we don't care if event handler cancels default behavior, since there is none
2379 dispatchDragSrcEvent(EventImpl::DRAG_EVENT, loc);
2383 void KWQKHTMLPart::dragSourceEndedAt(const QPoint &loc, NSDragOperation operation)
2385 if (!_dragSrc.isNull() && _dragSrcMayBeDHTML) {
2386 _dragClipboard->setDestinationOperation(operation);
2387 // for now we don't care if event handler cancels default behavior, since there is none
2388 dispatchDragSrcEvent(EventImpl::DRAGEND_EVENT, loc);
2394 // Returns whether caller should continue with "the default processing", which is the same as
2395 // the event handler NOT setting the return value to false
2396 bool KWQKHTMLPart::dispatchCPPEvent(int eventId, KWQClipboard::AccessPolicy policy)
2398 NodeImpl *target = d->m_selection.start().element();
2399 if (!target && xmlDocImpl()) {
2400 target = xmlDocImpl()->body();
2406 KWQClipboard *clipboard = new KWQClipboard(false, [NSPasteboard generalPasteboard], (KWQClipboard::AccessPolicy)policy);
2409 int exceptioncode = 0;
2410 EventImpl *evt = new ClipboardEventImpl(static_cast<EventImpl::EventId>(eventId), true, true, clipboard);
2412 target->dispatchEvent(evt, exceptioncode, true);
2413 bool noDefaultProcessing = evt->defaultPrevented();
2416 // invalidate clipboard here for security
2417 clipboard->setAccessPolicy(KWQClipboard::Numb);
2420 return !noDefaultProcessing;
2423 // WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items. They
2424 // also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items.
2425 // We need to use onbeforecopy as a real menu enabler because we allow elements that are not
2426 // normally selectable to implement copy/paste (like divs, or a document body).
2428 bool KWQKHTMLPart::mayCut()
2430 return !dispatchCPPEvent(EventImpl::BEFORECUT_EVENT, KWQClipboard::Numb);
2433 bool KWQKHTMLPart::mayCopy()
2435 return !dispatchCPPEvent(EventImpl::BEFORECOPY_EVENT, KWQClipboard::Numb);
2438 bool KWQKHTMLPart::mayPaste()
2440 return !dispatchCPPEvent(EventImpl::BEFOREPASTE_EVENT, KWQClipboard::Numb);
2443 bool KWQKHTMLPart::tryCut()
2445 // Must be done before oncut adds types and data to the pboard,
2446 // also done for security, as it erases data from the last copy/paste.
2447 [[NSPasteboard generalPasteboard] declareTypes:[NSArray array] owner:nil];
2449 return !dispatchCPPEvent(EventImpl::CUT_EVENT, KWQClipboard::Writable);
2452 bool KWQKHTMLPart::tryCopy()
2454 // Must be done before oncopy adds types and data to the pboard,
2455 // also done for security, as it erases data from the last copy/paste.
2456 [[NSPasteboard generalPasteboard] declareTypes:[NSArray array] owner:nil];
2458 return !dispatchCPPEvent(EventImpl::COPY_EVENT, KWQClipboard::Writable);
2461 bool KWQKHTMLPart::tryPaste()
2463 return !dispatchCPPEvent(EventImpl::PASTE_EVENT, KWQClipboard::Readable);
2466 void KWQKHTMLPart::khtmlMouseReleaseEvent(MouseReleaseEvent *event)
2468 NSView *view = mouseDownViewIfStillGood();
2470 // If this was the first click in the window, we don't even want to clear the selection.
2471 // This case occurs when the user clicks on a draggable element, since we have to process
2472 // the mouse down and drag events to see if we might start a drag. For other first clicks
2473 // in a window, we just don't acceptFirstMouse, and the whole down-drag-up sequence gets
2474 // ignored upstream of this layer.
2475 if (_activationEventNumber != [_currentEvent eventNumber]) {
2476 KHTMLPart::khtmlMouseReleaseEvent(event);
2481 _sendingEventToSubview = true;
2482 KWQ_BLOCK_EXCEPTIONS;
2483 [view mouseUp:_currentEvent];
2484 KWQ_UNBLOCK_EXCEPTIONS;
2485 _sendingEventToSubview = false;
2488 void KWQKHTMLPart::clearTimers(KHTMLView *view)
2491 view->unscheduleRelayout();
2493 DocumentImpl* document = view->part()->xmlDocImpl();
2494 if (document && document->renderer() && document->renderer()->layer())
2495 document->renderer()->layer()->suspendMarquees();
2500 void KWQKHTMLPart::clearTimers()
2502 clearTimers(d->m_view);
2505 bool KWQKHTMLPart::passSubframeEventToSubframe(NodeImpl::MouseEvent &event)
2507 KWQ_BLOCK_EXCEPTIONS;
2509 switch ([_currentEvent type]) {
2510 case NSLeftMouseDown: {
2511 NodeImpl *node = event.innerNode.handle();
2515 RenderPart *renderPart = dynamic_cast<RenderPart *>(node->renderer());
2519 if (!passWidgetMouseDownEventToWidget(renderPart)) {
2522 _mouseDownWasInSubframe = true;
2525 case NSLeftMouseUp: {
2526 if (!_mouseDownWasInSubframe) {
2529 NSView *view = mouseDownViewIfStillGood();
2533 ASSERT(!_sendingEventToSubview);
2534 _sendingEventToSubview = true;
2535 [view mouseUp:_currentEvent];
2536 _sendingEventToSubview = false;
2539 case NSLeftMouseDragged: {
2540 if (!_mouseDownWasInSubframe) {
2543 NSView *view = mouseDownViewIfStillGood();
2547 ASSERT(!_sendingEventToSubview);
2548 _sendingEventToSubview = true;
2549 [view mouseDragged:_currentEvent];
2550 _sendingEventToSubview = false;
2556 KWQ_UNBLOCK_EXCEPTIONS;
2561 void KWQKHTMLPart::mouseDown(NSEvent *event)
2563 KHTMLView *v = d->m_view;
2564 if (!v || _sendingEventToSubview) {
2568 KWQ_BLOCK_EXCEPTIONS;
2570 prepareForUserAction();
2572 _mouseDownView = nil;
2575 NSEvent *oldCurrentEvent = _currentEvent;
2576 _currentEvent = KWQRetain(event);
2577 NSPoint loc = [event locationInWindow];
2578 _mouseDownWinX = (int)loc.x;
2579 _mouseDownWinY = (int)loc.y;
2580 d->m_view->viewportToContents(_mouseDownWinX, _mouseDownWinY, _mouseDownX, _mouseDownY);
2581 _mouseDownTimestamp = [event timestamp];
2583 _mouseDownMayStartDrag = false;
2584 _mouseDownMayStartSelect = false;
2586 QMouseEvent kEvent(QEvent::MouseButtonPress, event);
2587 v->viewportMousePressEvent(&kEvent);
2589 ASSERT(_currentEvent == event);
2591 _currentEvent = oldCurrentEvent;
2593 KWQ_UNBLOCK_EXCEPTIONS;
2596 void KWQKHTMLPart::mouseDragged(NSEvent *event)
2598 KHTMLView *v = d->m_view;
2599 if (!v || _sendingEventToSubview) {
2603 KWQ_BLOCK_EXCEPTIONS;
2605 NSEvent *oldCurrentEvent = _currentEvent;
2606 _currentEvent = KWQRetain(event);
2608 QMouseEvent kEvent(QEvent::MouseMove, event);
2609 v->viewportMouseMoveEvent(&kEvent);
2611 ASSERT(_currentEvent == event);
2613 _currentEvent = oldCurrentEvent;
2615 KWQ_UNBLOCK_EXCEPTIONS;
2618 void KWQKHTMLPart::mouseUp(NSEvent *event)
2620 KHTMLView *v = d->m_view;
2621 if (!v || _sendingEventToSubview) {
2625 KWQ_BLOCK_EXCEPTIONS;
2627 NSEvent *oldCurrentEvent = _currentEvent;
2628 _currentEvent = KWQRetain(event);
2630 // Our behavior here is a little different that Qt. Qt always sends
2631 // a mouse release event, even for a double click. To correct problems
2632 // in khtml's DOM click event handling we do not send a release here
2633 // for a double click. Instead we send that event from KHTMLView's
2634 // viewportMouseDoubleClickEvent. Note also that the third click of
2635 // a triple click is treated as a single click, but the fourth is then
2636 // treated as another double click. Hence the "% 2" below.
2637 int clickCount = [event clickCount];
2638 if (clickCount > 0 && clickCount % 2 == 0) {
2639 QMouseEvent doubleClickEvent(QEvent::MouseButtonDblClick, event);
2640 v->viewportMouseDoubleClickEvent(&doubleClickEvent);
2642 QMouseEvent releaseEvent(QEvent::MouseButtonRelease, event);
2643 v->viewportMouseReleaseEvent(&releaseEvent);
2646 ASSERT(_currentEvent == event);
2648 _currentEvent = oldCurrentEvent;
2650 _mouseDownView = nil;
2652 KWQ_UNBLOCK_EXCEPTIONS;
2656 A hack for the benefit of AK's PopUpButton, which uses the Carbon menu manager, which thus
2657 eats all subsequent events after it is starts its modal tracking loop. After the interaction
2658 is done, this routine is used to fix things up. When a mouse down started us tracking in
2659 the widget, we post a fake mouse up to balance the mouse down we started with. When a
2660 key down started us tracking in the widget, we post a fake key up to balance things out.
2661 In addition, we post a fake mouseMoved to get the cursor in sync with whatever we happen to
2662 be over after the tracking is done.
2664 void KWQKHTMLPart::sendFakeEventsAfterWidgetTracking(NSEvent *initiatingEvent)
2666 KWQ_BLOCK_EXCEPTIONS;
2668 _sendingEventToSubview = false;
2669 int eventType = [initiatingEvent type];
2670 ASSERT(eventType == NSLeftMouseDown || eventType == NSKeyDown);
2671 NSEvent *fakeEvent = nil;
2672 if (eventType == NSLeftMouseDown) {
2673 fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
2674 location:[initiatingEvent locationInWindow]
2675 modifierFlags:[initiatingEvent modifierFlags]
2676 timestamp:[initiatingEvent timestamp]
2677 windowNumber:[initiatingEvent windowNumber]
2678 context:[initiatingEvent context]
2679 eventNumber:[initiatingEvent eventNumber]
2680 clickCount:[initiatingEvent clickCount]
2681 pressure:[initiatingEvent pressure]];
2685 else { // eventType == NSKeyDown
2686 fakeEvent = [NSEvent keyEventWithType:NSKeyUp
2687 location:[initiatingEvent locationInWindow]
2688 modifierFlags:[initiatingEvent modifierFlags]
2689 timestamp:[initiatingEvent timestamp]
2690 windowNumber:[initiatingEvent windowNumber]
2691 context:[initiatingEvent context]
2692 characters:[initiatingEvent characters]
2693 charactersIgnoringModifiers:[initiatingEvent charactersIgnoringModifiers]
2694 isARepeat:[initiatingEvent isARepeat]
2695 keyCode:[initiatingEvent keyCode]];
2696 keyEvent(fakeEvent);
2698 // FIXME: We should really get the current modifierFlags here, but there's no way to poll
2699 // them in Cocoa, and because the event stream was stolen by the Carbon menu code we have
2700 // no up-to-date cache of them anywhere.
2701 fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved
2702 location:[[_bridge window] convertScreenToBase:[NSEvent mouseLocation]]
2703 modifierFlags:[initiatingEvent modifierFlags]
2704 timestamp:[initiatingEvent timestamp]
2705 windowNumber:[initiatingEvent windowNumber]
2706 context:[initiatingEvent context]
2710 mouseMoved(fakeEvent);
2712 KWQ_UNBLOCK_EXCEPTIONS;
2715 void KWQKHTMLPart::mouseMoved(NSEvent *event)
2717 KHTMLView *v = d->m_view;
2718 // Reject a mouse moved if the button is down - screws up tracking during autoscroll
2719 // These happen because WebKit sometimes has to fake up moved events.
2720 if (!v || d->m_bMousePressed) {
2724 KWQ_BLOCK_EXCEPTIONS;
2726 NSEvent *oldCurrentEvent = _currentEvent;
2727 _currentEvent = KWQRetain(event);
2729 QMouseEvent kEvent(QEvent::MouseMove, event);
2730 v->viewportMouseMoveEvent(&kEvent);
2732 ASSERT(_currentEvent == event);
2734 _currentEvent = oldCurrentEvent;
2736 KWQ_UNBLOCK_EXCEPTIONS;
2739 // Called as we walk up the element chain for nodes with CSS property -khtml-user-drag == auto
2740 bool KWQKHTMLPart::shouldDragAutoNode(DOM::NodeImpl* node, int x, int y) const
2742 // We assume that WebKit only cares about dragging things that can be leaf nodes (text, images, urls).
2743 // This saves a bunch of expensive calls (creating WC and WK element dicts) as we walk farther up
2744 // the node hierarchy, and we also don't have to cook up a way to ask WK about non-leaf nodes
2745 // (since right now WK just hit-tests using a cached lastMouseDown).
2746 if (!node->hasChildNodes() && d->m_view) {
2747 int windowX, windowY;
2748 d->m_view->contentsToViewport(x, y, windowX, windowY);
2749 NSPoint eventLoc = {windowX, windowY};
2750 return [_bridge mayStartDragAtEventLocation:eventLoc];
2756 bool KWQKHTMLPart::sendContextMenuEvent(NSEvent *event)
2758 DocumentImpl *doc = d->m_doc;
2759 KHTMLView *v = d->m_view;
2764 KWQ_BLOCK_EXCEPTIONS;
2766 NSEvent *oldCurrentEvent = _currentEvent;
2767 _currentEvent = KWQRetain(event);
2769 QMouseEvent qev(QEvent::MouseButtonPress, event);
2772 v->viewportToContents(qev.x(), qev.y(), xm, ym);
2774 NodeImpl::MouseEvent mev(qev.stateAfter(), NodeImpl::MousePress);
2775 doc->prepareMouseEvent(false, xm, ym, &mev);
2777 bool swallowEvent = v->dispatchMouseEvent(EventImpl::CONTEXTMENU_EVENT,
2778 mev.innerNode.handle(), true, 0, &qev, true, NodeImpl::MousePress);
2779 if (!swallowEvent && ([_bridge isEditable] || mev.innerNode.handle()->isContentEditable()) && !isPointInsideSelection(xm, ym)) {
2780 selectClosestWordFromMouseEvent(&qev, mev.innerNode, xm, ym);
2783 ASSERT(_currentEvent == event);
2785 _currentEvent = oldCurrentEvent;
2787 return swallowEvent;
2789 KWQ_UNBLOCK_EXCEPTIONS;
2794 struct ListItemInfo {
2799 NSFileWrapper *KWQKHTMLPart::fileWrapperForElement(ElementImpl *e)
2801 KWQ_BLOCK_EXCEPTIONS;
2803 NSFileWrapper *wrapper = nil;
2805 AtomicString attr = e->getAttribute(ATTR_SRC);
2806 if (!attr.isEmpty()) {
2807 NSURL *URL = completeURL(attr.string()).getNSURL();
2808 wrapper = [_bridge fileWrapperForURL:URL];
2811 RenderImage *renderer = static_cast<RenderImage *>(e->renderer());
2812 NSImage *image = renderer->pixmap().image();
2813 NSData *tiffData = [image TIFFRepresentationUsingCompression:NSTIFFCompressionLZW factor:0.0];
2814 wrapper = [[NSFileWrapper alloc] initRegularFileWithContents:tiffData];
2815 [wrapper setPreferredFilename:@"image.tiff"];
2816 [wrapper autorelease];
2821 KWQ_UNBLOCK_EXCEPTIONS;
2826 static ElementImpl *listParent(ElementImpl *item)
2828 // Ick! Avoid use of item->id() which confuses ObjC++.
2829 unsigned short _id = Node(item).elementId();
2831 while (_id != ID_UL && _id != ID_OL) {
2832 item = static_cast<ElementImpl *>(item->parentNode());
2835 _id = Node(item).elementId();
2840 static NodeImpl* isTextFirstInListItem(NodeImpl *e)
2842 if (Node(e).nodeType() != Node::TEXT_NODE)
2844 NodeImpl* par = e->parentNode();
2846 if (par->firstChild() != e)
2848 if (Node(par).elementId() == ID_LI)
2851 par = par->parentNode();
2856 // FIXME: This collosal function needs to be refactored into maintainable smaller bits.
2858 #define BULLET_CHAR 0x2022
2859 #define SQUARE_CHAR 0x25AA
2860 #define CIRCLE_CHAR 0x25E6
2862 NSAttributedString *KWQKHTMLPart::attributedString(NodeImpl *_start, int startOffset, NodeImpl *endNode, int endOffset)
2864 KWQ_BLOCK_EXCEPTIONS;
2866 NodeImpl * _startNode = _start;
2868 if (_startNode == nil) {
2872 NSMutableAttributedString *result = [[[NSMutableAttributedString alloc] init] autorelease];
2874 bool hasNewLine = true;
2875 bool addedSpace = true;
2876 NSAttributedString *pendingStyledSpace = nil;
2877 bool hasParagraphBreak = true;
2878 const ElementImpl *linkStartNode = 0;
2879 unsigned linkStartLocation = 0;
2880 QPtrList<ElementImpl> listItems;
2881 QValueList<ListItemInfo> listItemLocations;
2882 float maxMarkerWidth = 0;
2884 Node n = _startNode;
2886 // If the first item is the entire text of a list item, use the list item node as the start of the
2887 // selection, not the text node. The user's intent was probably to select the list.
2888 if (n.nodeType() == Node::TEXT_NODE && startOffset == 0) {
2889 NodeImpl *startListNode = isTextFirstInListItem(_startNode);
2891 _startNode = startListNode;
2896 while (!n.isNull()) {
2897 RenderObject *renderer = n.handle()->renderer();
2899 RenderStyle *style = renderer->style();
2900 NSFont *font = style->font().getNSFont();
2901 bool needSpace = pendingStyledSpace != nil;
2902 if (n.nodeType() == Node::TEXT_NODE) {
2906 [pendingStyledSpace release];
2907 pendingStyledSpace = nil;
2911 QString str = n.nodeValue().string();
2912 int start = (n == _startNode) ? startOffset : -1;
2913 int end = (n == endNode) ? endOffset : -1;
2914 if (renderer->isText()) {
2915 if (renderer->style()->whiteSpace() == PRE) {
2916 if (needSpace && !addedSpace) {
2917 if (text.isEmpty() && linkStartLocation == [result length]) {
2918 ++linkStartLocation;
2920 [result appendAttributedString:pendingStyledSpace];
2922 int runStart = (start == -1) ? 0 : start;
2923 int runEnd = (end == -1) ? str.length() : end;
2924 text += str.mid(runStart, runEnd-runStart);
2925 [pendingStyledSpace release];
2926 pendingStyledSpace = nil;
2927 addedSpace = str[runEnd-1].direction() == QChar::DirWS;
2930 RenderText* textObj = static_cast<RenderText*>(renderer);
2931 if (!textObj->firstTextBox() && str.length() > 0 && !addedSpace) {
2932 // We have no runs, but we do have a length. This means we must be
2933 // whitespace that collapsed away at the end of a line.
2939 for (InlineTextBox* box = textObj->firstTextBox(); box; box = box->nextTextBox()) {
2940 int runStart = (start == -1) ? box->m_start : start;
2941 int runEnd = (end == -1) ? box->m_start + box->m_len : end;
2942 runEnd = kMin(runEnd, box->m_start + box->m_len);
2943 if (runStart >= box->m_start &&
2944 runStart < box->m_start + box->m_len) {
2945 if (box == textObj->firstTextBox() && box->m_start == runStart && runStart > 0) {
2946 needSpace = true; // collapsed space at the start
2948 if (needSpace && !addedSpace) {
2949 if (pendingStyledSpace != nil) {
2950 if (text.isEmpty() && linkStartLocation == [result length]) {
2951 ++linkStartLocation;
2953 [result appendAttributedString:pendingStyledSpace];
2958 QString runText = str.mid(runStart, runEnd - runStart);
2959 runText.replace('\n', ' ');
2961 int nextRunStart = box->nextTextBox() ? box->nextTextBox()->m_start : str.length(); // collapsed space between runs or at the end
2962 needSpace = nextRunStart > runEnd;
2963 [pendingStyledSpace release];
2964 pendingStyledSpace = nil;
2965 addedSpace = str[runEnd-1].direction() == QChar::DirWS;
2968 if (end != -1 && runEnd >= end)
2975 text.replace(QChar('\\'), renderer->backslashAsCurrencySymbol());
2977 if (text.length() > 0 || needSpace) {
2978 NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init];
2979 [attrs setObject:font forKey:NSFontAttributeName];
2980 if (style && style->color().isValid() && qAlpha(style->color().rgb()) != 0)
2981 [attrs setObject:style->color().getNSColor() forKey:NSForegroundColorAttributeName];
2982 if (style && style->backgroundColor().isValid() && qAlpha(style->backgroundColor().rgb()) != 0)
2983 [attrs setObject:style->backgroundColor().getNSColor() forKey:NSBackgroundColorAttributeName];
2985 if (text.length() > 0) {
2986 hasParagraphBreak = false;
2987 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString() attributes:attrs];
2988 [result appendAttributedString: partialString];
2989 [partialString release];
2993 [pendingStyledSpace release];
2994 pendingStyledSpace = [[NSAttributedString alloc] initWithString:@" " attributes:attrs];
3000 // This is our simple HTML -> ASCII transformation:
3002 unsigned short _id = n.elementId();
3005 // Note the start of the <a> element. We will add the NSLinkAttributeName
3006 // attribute to the attributed string when navigating to the next sibling
3008 linkStartLocation = [result length];
3009 linkStartNode = static_cast<ElementImpl*>(n.handle());
3020 ElementImpl *itemParent = listParent(static_cast<ElementImpl *>(n.handle()));
3026 listItems.append(static_cast<ElementImpl*>(n.handle()));
3028 info.start = [result length];
3030 listItemLocations.append (info);
3033 if (itemParent && renderer->isListItem()) {
3034 RenderListItem *listRenderer = static_cast<RenderListItem*>(renderer);
3036 maxMarkerWidth = MAX([font pointSize], maxMarkerWidth);
3037 switch(listRenderer->style()->listStyleType()) {
3039 listText += ((QChar)BULLET_CHAR);
3042 listText += ((QChar)CIRCLE_CHAR);
3045 listText += ((QChar)SQUARE_CHAR);
3050 QString marker = listRenderer->markerStringValue();
3052 // Use AppKit metrics. Will be rendered by AppKit.
3053 float markerWidth = [font widthOfString: marker.getNSString()];
3054 maxMarkerWidth = MAX(markerWidth, maxMarkerWidth);
3060 NSMutableDictionary *attrs;
3062 attrs = [[NSMutableDictionary alloc] init];
3063 [attrs setObject:font forKey:NSFontAttributeName];
3064 if (style && style->color().isValid())
3065 [attrs setObject:style->color().getNSColor() forKey:NSForegroundColorAttributeName];
3066 if (style && style->backgroundColor().isValid())
3067 [attrs setObject:style->backgroundColor().getNSColor() forKey:NSBackgroundColorAttributeName];
3069 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:listText.getNSString() attributes:attrs];
3071 [result appendAttributedString: partialString];
3072 [partialString release];
3107 if (!hasParagraphBreak) {
3109 hasParagraphBreak = true;
3115 if (pendingStyledSpace != nil) {
3116 if (linkStartLocation == [result length]) {
3117 ++linkStartLocation;
3119 [result appendAttributedString:pendingStyledSpace];
3120 [pendingStyledSpace release];
3121 pendingStyledSpace = nil;
3123 NSFileWrapper *fileWrapper = fileWrapperForElement(static_cast<ElementImpl *>(n.handle()));
3124 NSTextAttachment *attachment = [[NSTextAttachment alloc] initWithFileWrapper:fileWrapper];
3125 NSAttributedString *iString = [NSAttributedString attributedStringWithAttachment:attachment];
3126 [result appendAttributedString: iString];
3127 [attachment release];
3130 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()];
3131 [result appendAttributedString: partialString];
3132 [partialString release];
3139 Node next = n.firstChild();
3141 next = n.nextSibling();
3144 while (next.isNull() && !n.parentNode().isNull()) {
3149 next = n.nextSibling();
3151 unsigned short _id = n.elementId();
3154 // End of a <a> element. Create an attributed string NSLinkAttributeName attribute
3155 // for the range of the link. Note that we create the attributed string from the DOM, which
3156 // will have corrected any illegally nested <a> elements.
3157 if (linkStartNode && n.handle() == linkStartNode){
3158 DOMString href = parseURL(linkStartNode->getAttribute(ATTR_HREF));
3159 KURL kURL = KWQ(linkStartNode->getDocument()->part())->completeURL(href.string());
3161 NSURL *URL = kURL.getNSURL();
3162 [result addAttribute:NSLinkAttributeName value:URL range:NSMakeRange(linkStartLocation, [result length]-linkStartLocation)];
3176 int i, count = listItems.count();
3177 for (i = 0; i < count; i++){
3178 if (listItems.at(i) == n.handle()){
3179 listItemLocations[i].end = [result length];
3212 // An extra newline is needed at the start, not the end, of these types of tags,
3213 // so don't add another here.
3218 NSAttributedString *partialString = [[NSAttributedString alloc] initWithString:text.getNSString()];
3219 [result appendAttributedString:partialString];
3220 [partialString release];
3226 [pendingStyledSpace release];
3228 // Apply paragraph styles from outside in. This ensures that nested lists correctly
3229 // override their parent's paragraph style.
3231 unsigned i, count = listItems.count();
3235 #ifdef POSITION_LIST
3236 NodeImpl *containingBlock;
3237 int containingBlockX, containingBlockY;
3239 // Determine the position of the outermost containing block. All paragraph
3240 // styles and tabs should be relative to this position. So, the horizontal position of
3241 // each item in the list (in the resulting attributed string) will be relative to position
3242 // of the outermost containing block.
3244 containingBlock = _startNode;
3245 while (containingBlock->renderer()->isInline()){
3246 containingBlock = containingBlock->parentNode();
3248 containingBlock->renderer()->absolutePosition(containingBlockX, containingBlockY);
3252 for (i = 0; i < count; i++){
3253 e = listItems.at(i);
3254 info = listItemLocations[i];
3256 if (info.end < info.start)
3257 info.end = [result length];
3259 RenderObject *r = e->renderer();
3260 RenderStyle *style = r->style();
3263 NSFont *font = style->font().getNSFont();
3264 float pointSize = [font pointSize];
3266 #ifdef POSITION_LIST
3268 r->absolutePosition(rx, ry);
3269 rx -= containingBlockX;
3271 // Ensure that the text is indented at least enough to allow for the markers.
3272 rx = MAX(rx, (int)maxMarkerWidth);
3274 rx = (int)MAX(maxMarkerWidth, pointSize);
3277 // The bullet text will be right aligned at the first tab marker, followed
3278 // by a space, followed by the list item text. The space is arbitrarily
3279 // picked as pointSize*2/3. The space on the first line of the text item
3280 // is established by a left aligned tab, on subsequent lines it's established
3281 // by the head indent.
3282 NSMutableParagraphStyle *mps = [[NSMutableParagraphStyle alloc] init];
3283 [mps setFirstLineHeadIndent: 0];
3284 [mps setHeadIndent: rx];
3285 [mps setTabStops:[NSArray arrayWithObjects:
3286 [[[NSTextTab alloc] initWithType:NSRightTabStopType location:rx-(pointSize*2/3)] autorelease],
3287 [[[NSTextTab alloc] initWithType:NSLeftTabStopType location:rx] autorelease],
3289 [result addAttribute:NSParagraphStyleAttributeName value:mps range:NSMakeRange(info.start, info.end-info.start)];
3296 KWQ_UNBLOCK_EXCEPTIONS;
3301 QRect KWQKHTMLPart::selectionRect() const
3307 RenderCanvas *root = static_cast<RenderCanvas *>(xmlDocImpl()->renderer());
3312 return root->selectionRect();
3315 // returns NSRect because going through QRect would truncate any floats
3316 NSRect KWQKHTMLPart::visibleSelectionRect() const
3321 NSView *documentView = d->m_view->getDocumentView();
3322 if (!documentView) {
3325 return NSIntersectionRect(selectionRect(), [documentView visibleRect]);
3328 void KWQKHTMLPart::centerSelectionInVisibleArea() const
3330 switch (selection().state()) {
3331 case Selection::NONE:
3333 case Selection::CARET: {
3335 // passing true forces centering even if selection is already exposed
3336 view()->ensureRectVisibleCentered(selection().caretRect(), true);
3339 case Selection::RANGE:
3341 // passing true forces centering even if selection is already exposed
3342 view()->ensureRectVisibleCentered(selectionRect(), true);
3347 NSImage *KWQKHTMLPart::imageFromRect(NSRect rect) const
3349 NSView *view = d->m_view->getDocumentView();
3354 NSRect bounds = [view bounds];
3355 NSImage *resultImage = [[[NSImage alloc] initWithSize:rect.size] autorelease];
3357 KWQ_BLOCK_EXCEPTIONS;
3359 if (rect.size.width != 0 && rect.size.height != 0) {
3360 [resultImage setFlipped:YES];
3361 [resultImage lockFocus];
3363 [NSGraphicsContext saveGraphicsState];
3364 NSPoint translation = { -(NSMinX(rect) - NSMinX(bounds)), -(NSMinY(rect) - NSMinY(bounds)) };
3365 CGContextTranslateCTM((CGContext *)[[NSGraphicsContext currentContext] graphicsPort], translation.x, translation.y);
3367 // We change the coord system at the CG level, out from under the AK focus machinery, because it doesn't
3368 // work to change the coord system of a focused view. However, WebImageRenderer uses the difference
3369 // between the focused view's coord system and the window's coord system to adjust the pattern phase, and
3370 // that calc ignores our translation. So we must tell it about this extra phase offset.
3372 // Window is not flipped, we are, so y coord must be inverted when describing phase, which is a
3373 // window level notion.
3374 translation.y = -translation.y;
3375 [[WebCoreGraphicsBridge sharedBridge] setAdditionalPatternPhase:translation];
3377 [view drawRect:rect];
3379 [[WebCoreGraphicsBridge sharedBridge] setAdditionalPatternPhase:NSZeroPoint];
3380 [NSGraphicsContext restoreGraphicsState];
3382 [resultImage unlockFocus];
3383 [resultImage setFlipped:NO];
3386 KWQ_UNBLOCK_EXCEPTIONS;
3391 NSImage *KWQKHTMLPart::selectionImage() const
3393 _drawSelectionOnly = true; // invoke special drawing mode
3394 NSImage *result = imageFromRect(visibleSelectionRect());
3395 _drawSelectionOnly = false;
3399 NSImage *KWQKHTMLPart::snapshotDragImage(DOM::Node node, NSRect *imageRect, NSRect *elementRect) const
3401 RenderObject *renderer = node.handle()->renderer();
3406 renderer->updateDragState(true); // mark dragged nodes (so they pick up the right CSS)
3407 d->m_doc->updateLayout(); // forces style recalc - needed since changing the drag state might
3408 // imply new styles, plus JS could have changed other things
3410 NSRect paintingRect = renderer->paintingRootRect(topLevelRect);
3412 _elementToDraw = node; // invoke special sub-tree drawing mode
3413 NSImage *result = imageFromRect(paintingRect);
3414 renderer->updateDragState(false);
3418 *elementRect = topLevelRect;
3421 *imageRect = paintingRect;
3426 RenderStyle *KWQKHTMLPart::styleForSelectionStart(NodeImpl *&nodeToRemove) const
3432 if (d->m_selection.isNone())
3435 Position pos = VisiblePosition(d->m_selection.start(), UPSTREAM).deepEquivalent();
3436 if (!pos.inRenderedContent())
3438 NodeImpl *node = pos.node();
3442 if (!d->m_typingStyle)
3443 return node->renderer()->style();
3445 int exceptionCode = 0;
3446 ElementImpl *styleElement = xmlDocImpl()->createHTMLElement("span", exceptionCode);
3447 ASSERT(exceptionCode == 0);
3449 styleElement->setAttribute(ATTR_STYLE, d->m_typingStyle->cssText().implementation(), exceptionCode);
3450 ASSERT(exceptionCode == 0);
3452 TextImpl *text = xmlDocImpl()->createEditingTextNode("");
3453 styleElement->appendChild(text, exceptionCode);
3454 ASSERT(exceptionCode == 0);
3456 node->parentNode()->appendChild(styleElement, exceptionCode);
3457 ASSERT(exceptionCode == 0);
3459 nodeToRemove = styleElement;
3460 return styleElement->renderer()->style();
3463 NSFont *KWQKHTMLPart::fontForSelection(bool *hasMultipleFonts) const
3465 if (hasMultipleFonts)
3466 *hasMultipleFonts = false;
3468 if (!d->m_selection.isRange()) {
3469 NodeImpl *nodeToRemove;
3470 RenderStyle *style = styleForSelectionStart(nodeToRemove); // sets nodeToRemove
3474 result = style->font().getNSFont();
3478 nodeToRemove->remove(exceptionCode);
3479 ASSERT(exceptionCode == 0);
3487 Range r = d->m_selection.toRange();
3488 RangeImpl *range = r.handle();
3489 NodeImpl *pastEnd = range->pastEndNode();
3490 for (NodeImpl *n = range->startNode(); n != pastEnd; n = n->traverseNextNode()) {
3491 RenderObject *renderer = n->renderer();
3494 // FIXME: Are there any node types that have renderers, but that we should be skipping?
3495 NSFont *f = renderer->style()->font().getNSFont();
3498 if (!hasMultipleFonts)
3500 } else if (font != f) {
3501 *hasMultipleFonts = true;
3509 NSDictionary *KWQKHTMLPart::fontAttributesForSelectionStart() const
3511 NodeImpl *nodeToRemove;
3512 RenderStyle *style = styleForSelectionStart(nodeToRemove);
3516 NSMutableDictionary *result = [NSMutableDictionary dictionary];
3518 if (style->backgroundColor().isValid() && style->backgroundColor().alpha() != 0)
3519 [result setObject:style->backgroundColor().getNSColor() forKey:NSBackgroundColorAttributeName];
3521 if (style->font().getNSFont())
3522 [result setObject:style->font().getNSFont() forKey:NSFontAttributeName];
3524 if (style->color().isValid() && style->color() != black)
3525 [result setObject:style->color().getNSColor() forKey:NSForegroundColorAttributeName];
3527 ShadowData *shadow = style->textShadow();
3529 NSShadow *s = [[NSShadow alloc] init];
3530 [s setShadowOffset:NSMakeSize(shadow->x, shadow->y)];
3531 [s setShadowBlurRadius:shadow->blur];
3532 [s setShadowColor:shadow->color.getNSColor()];
3533 [result setObject:s forKey:NSShadowAttributeName];
3536 int decoration = style->textDecorationsInEffect();
3537 if (decoration & khtml::LINE_THROUGH)
3538 [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSStrikethroughStyleAttributeName];
3540 int superscriptInt = 0;
3541 switch (style->verticalAlign()) {
3542 case khtml::BASELINE:
3544 case khtml::BASELINE_MIDDLE:
3547 case khtml::TEXT_BOTTOM:
3548 case khtml::TEXT_TOP:
3552 superscriptInt = -1;
3559 [result setObject:[NSNumber numberWithInt:superscriptInt] forKey:NSSuperscriptAttributeName];
3561 if (decoration & khtml::UNDERLINE)
3562 [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
3565 int exceptionCode = 0;
3566 nodeToRemove->remove(exceptionCode);
3567 ASSERT(exceptionCode == 0);
3573 KWQWindowWidget *KWQKHTMLPart::topLevelWidget()
3575 return _windowWidget;
3578 void KWQKHTMLPart::tokenizerProcessedData()
3581 [_bridge tokenizerProcessedData];
3584 int KWQKHTMLPart::selectionStartOffset() const
3586 return d->m_selection.start().offset();
3589 int KWQKHTMLPart::selectionEndOffset() const
3591 return d->m_selection.end().offset();
3594 NodeImpl *KWQKHTMLPart::selectionStart() const
3596 return d->m_selection.start().node();
3599 NodeImpl *KWQKHTMLPart::selectionEnd() const
3601 return d->m_selection.end().node();
3604 void KWQKHTMLPart::setBridge(WebCoreBridge *p)
3607 delete _windowWidget;
3610 _windowWidget = new KWQWindowWidget(_bridge);
3613 QString KWQKHTMLPart::overrideMediaType() const
3615 NSString *overrideType = [_bridge overrideMediaType];
3617 return QString::fromNSString(overrideType);
3621 void KWQKHTMLPart::setMediaType(const QString &type)
3624 d->m_view->setMediaType(type);
3628 void KWQKHTMLPart::setSelectionFromNone()
3630 // Put the caret someplace if the selection is empty and the part is editable.
3631 // This has the effect of flashing the caret in a contentEditable view automatically
3632 // without requiring the programmer to set a selection explicitly.
3633 DocumentImpl *doc = xmlDocImpl();
3634 if (doc && selection().isNone() && isContentEditable()) {
3635 NodeImpl *node = doc->documentElement();
3637 // Look for a block flow, but skip over the HTML element, since we really
3638 // want to get at least as far as the the BODY element in a document.
3639 if (node->isBlockFlow() && node->identifier() != ID_HTML)
3641 node = node->traverseNextNode();
3644 setSelection(Position(node, 0));
3648 void KWQKHTMLPart::setDisplaysWithFocusAttributes(bool flag)
3650 if (d->m_isFocused == flag)
3652 d->m_isFocused = flag;
3654 // This method does the job of updating the view based on whether the view is "active".
3655 // This involves three kinds of drawing updates:
3657 // 1. The background color used to draw behind selected content (active | inactive color)
3659 d->m_view->updateContents(QRect(visibleSelectionRect()));
3661 // 2. Caret blinking (blinks | does not blink)
3663 setSelectionFromNone();
3664 setCaretVisible(d->m_isFocused);
3666 // 3. The drawing of a focus ring around links in web pages.
3667 DocumentImpl *doc = xmlDocImpl();
3669 NodeImpl *node = doc->focusNode();
3670 if (node && node->renderer())
3671 node->renderer()->repaint();
3674 if (doc && doc->body()) {
3676 // Send onfocus event.
3677 doc->body()->dispatchWindowEvent(EventImpl::FOCUS_EVENT, false, false);
3680 // Send onblur event.
3681 doc->body()->dispatchWindowEvent(EventImpl::BLUR_EVENT, false, false);
3686 bool KWQKHTMLPart::displaysWithFocusAttributes() const
3688 return d->m_isFocused;
3691 QChar KWQKHTMLPart::backslashAsCurrencySymbol() const
3693 DocumentImpl *doc = xmlDocImpl();
3697 Decoder *decoder = doc->decoder();
3701 const QTextCodec *codec = decoder->codec();
3705 return codec->backslashAsCurrencySymbol();
3708 NSColor *KWQKHTMLPart::bodyBackgroundColor() const
3710 if (xmlDocImpl() && xmlDocImpl()->body() && xmlDocImpl()->body()->renderer()) {
3711 QColor bgColor = xmlDocImpl()->body()->renderer()->style()->backgroundColor();
3712 if (bgColor.isValid()) {
3713 return bgColor.getNSColor();
3719 WebCoreKeyboardUIMode KWQKHTMLPart::keyboardUIMode() const
3721 KWQ_BLOCK_EXCEPTIONS;
3722 return [_bridge keyboardUIMode];
3723 KWQ_UNBLOCK_EXCEPTIONS;
3725 return WebCoreKeyboardAccessDefault;
3728 void KWQKHTMLPart::setName(const QString &name)
3732 KWQKHTMLPart *parent = KWQ(parentPart());
3734 if (parent && (name.isEmpty() || parent->frameExists(name))) {
3735 n = parent->requestFrameName();
3738 KHTMLPart::setName(n);
3740 KWQ_BLOCK_EXCEPTIONS;
3741 [_bridge didSetName:n.getNSString()];
3742 KWQ_UNBLOCK_EXCEPTIONS;
3745 void KWQKHTMLPart::didTellBridgeAboutLoad(const QString &urlString)
3748 urlsBridgeKnowsAbout.insert(urlString, &dummy);
3751 bool KWQKHTMLPart::haveToldBridgeAboutLoad(const QString &urlString)
3753 return urlsBridgeKnowsAbout.find(urlString) != 0;
3756 void KWQKHTMLPart::clear()
3758 urlsBridgeKnowsAbout.clear();
3759 setMarkedTextRange(0);
3763 void KHTMLPart::print()
3765 [KWQ(this)->_bridge print];
3768 KJS::Bindings::Instance *KWQKHTMLPart::getAppletInstanceForView (NSView *aView)
3772 // Get a pointer to the actual Java applet instance.
3773 if ([_bridge respondsToSelector:@selector(getAppletInView:)])
3774 applet = [_bridge getAppletInView:aView];
3776 applet = [_bridge pollForAppletInView:aView];
3779 // Wrap the Java instance in a language neutral binding and hand
3780 // off ownership to the APPLET element.
3781 KJS::Bindings::Instance *instance = KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::JavaLanguage, applet);
3783 KJS::Bindings::RootObject *root = KJS::Bindings::RootObject::findRootObjectForNativeHandleFunction ()(aView);
3784 instance->setExecutionContext (root);
3792 @interface NSObject (WebPlugIn)
3793 - (id)objectForWebScript;
3794 - (void *)pluginScriptableObject;
3797 KJS::Bindings::Instance *KWQKHTMLPart::getEmbedInstanceForView (NSView *aView)
3799 if ([aView respondsToSelector:@selector(objectForWebScript)]){
3800 id object = [aView objectForWebScript];
3802 return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::ObjectiveCLanguage, object);
3804 else if ([aView respondsToSelector:@selector(pluginScriptableObject)]){
3805 void *object = [aView pluginScriptableObject];
3807 return KJS::Bindings::Instance::createBindingForLanguageInstance (KJS::Bindings::Instance::CLanguage, object);
3812 void KWQKHTMLPart::addPluginRootObject(const KJS::Bindings::RootObject *root)
3814 rootObjects.append (root);
3817 void KWQKHTMLPart::cleanupPluginRootObjects()
3819 KJS::Bindings::RootObject *root;
3820 while ((root = rootObjects.getLast())) {
3821 root->removeAllNativeReferences ();
3822 rootObjects.removeLast();
3826 void KWQKHTMLPart::registerCommandForUndoOrRedo(const EditCommandPtr &cmd, bool isRedo)
3829 KWQEditCommand *kwq = [KWQEditCommand commandWithEditCommand:cmd.get()];
3830 NSUndoManager *undoManager = [_bridge undoManager];
3831 [undoManager registerUndoWithTarget:_bridge selector:(isRedo ? @selector(redoEditing:) : @selector(undoEditing:)) object:kwq];
3832 NSString *actionName = [_bridge nameForUndoAction:static_cast<WebUndoAction>(cmd.editingAction())];
3833 if (actionName != nil) {
3834 [undoManager setActionName:actionName];
3836 _haveUndoRedoOperations = YES;
3839 void KWQKHTMLPart::registerCommandForUndo(const EditCommandPtr &cmd)
3841 registerCommandForUndoOrRedo(cmd, NO);
3844 void KWQKHTMLPart::registerCommandForRedo(const EditCommandPtr &cmd)
3846 registerCommandForUndoOrRedo(cmd, YES);
3849 void KWQKHTMLPart::clearUndoRedoOperations()
3851 if (_haveUndoRedoOperations) {
3852 [[_bridge undoManager] removeAllActionsWithTarget:_bridge];
3853 _haveUndoRedoOperations = NO;
3857 void KWQKHTMLPart::issueUndoCommand()
3860 [[_bridge undoManager] undo];
3863 void KWQKHTMLPart::issueRedoCommand()
3866 [[_bridge undoManager] redo];
3869 void KWQKHTMLPart::issueCutCommand()
3871 [_bridge issueCutCommand];
3874 void KWQKHTMLPart::issueCopyCommand()
3876 [_bridge issueCopyCommand];
3879 void KWQKHTMLPart::issuePasteCommand()
3881 [_bridge issuePasteCommand];
3884 bool KHTMLPart::canUndo() const
3886 return [[KWQ(this)->_bridge undoManager] canUndo];
3889 bool KHTMLPart::canRedo() const
3891 return [[KWQ(this)->_bridge undoManager] canRedo];
3894 bool KHTMLPart::canPaste() const
3896 return [KWQ(this)->_bridge canPaste];
3899 void KWQKHTMLPart::markMisspellingsInAdjacentWords(const VisiblePosition &p)
3901 if (![_bridge isContinuousSpellCheckingEnabled])
3903 markMisspellings(Selection(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)));
3906 void KWQKHTMLPart::markMisspellings(const Selection &selection)
3908 // This function is called with a selection already expanded to word boundaries.
3909 // Might be nice to assert that here.
3911 if (![_bridge isContinuousSpellCheckingEnabled])
3914 Range searchRange(selection.toRange());
3915 if (searchRange.isNull() || searchRange.isDetached())
3918 // If we're not in an editable node, bail.
3919 NodeImpl *editableNodeImpl = searchRange.startContainer().handle();
3920 if (!editableNodeImpl->isContentEditable())
3923 NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
3924 WordAwareIterator it(searchRange);
3926 while (!it.atEnd()) { // we may be starting at the end of the doc, and already by atEnd
3927 const QChar *chars = it.characters();
3928 long len = it.length();
3929 if (len > 1 || !chars[0].isSpace()) {
3930 NSString *chunk = [[NSString alloc] initWithCharactersNoCopy:(unichar *)chars length:len freeWhenDone:NO];
3932 // Loop over the chunk to find each misspelling in it.
3933 while (startIndex < len) {
3934 NSRange misspelling = [checker checkSpellingOfString:chunk startingAt:startIndex language:nil wrap:NO inSpellDocumentWithTag:[_bridge spellCheckerDocumentTag] wordCount:NULL];
3935 if (misspelling.length == 0) {
3939 // Build up result range and string. Note the misspelling may span many text nodes,
3940 // but the CharIterator insulates us from this complexity
3941 Range misspellingRange(xmlDocImpl());
3942 CharacterIterator chars(it.range());
3943 chars.advance(misspelling.location);
3944 misspellingRange.setStart(chars.range().startContainer(), chars.range().startOffset());
3945 chars.advance(misspelling.length);
3946 misspellingRange.setEnd(chars.range().startContainer(), chars.range().startOffset());
3947 // Mark misspelling in document.
3948 xmlDocImpl()->addMarker(misspellingRange, DocumentMarker::Spelling);
3949 startIndex = misspelling.location + misspelling.length;
3959 void KWQKHTMLPart::respondToChangedSelection(const Selection &oldSelection, bool closeTyping)
3962 if ([_bridge isContinuousSpellCheckingEnabled]) {
3963 VisiblePosition oldStart(oldSelection.start());
3964 Selection oldAdjacentWords(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
3966 VisiblePosition newStart(selection().start());
3967 Selection newAdjacentWords(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
3969 if (oldAdjacentWords != newAdjacentWords) {
3970 // Mark misspellings in the portion that was previously unmarked because of
3971 // the proximity of the start of the selection. We only spell check words in
3972 // the vicinity of the start of the old selection because the spelling checker
3973 // is not fast enough to do a lot of spelling checking implicitly. This matches
3974 // AppKit. This function is really the only code that knows that rule. The
3975 // markMisspellings function is prepared to handler larger ranges.
3977 // When typing we check spelling elsewhere, so don't redo it here.
3979 markMisspellings(oldAdjacentWords);
3982 // This only erases a marker in the first word of the selection.
3983 // Perhaps peculiar, but it matches AppKit.
3984 xmlDocImpl()->removeMarker(newAdjacentWords.toRange(), DocumentMarker::Spelling);
3987 // When continuous spell checking is off, no markers appear after the selection changes.
3988 xmlDocImpl()->removeAllMarkers();
3992 [_bridge respondToChangedSelection];
3995 void KWQKHTMLPart::respondToChangedContents()
3998 if (KWQAccObjectCache::accessibilityEnabled()) {
3999 renderer()->document()->getAccObjectCache()->postNotificationToTopWebArea(renderer(), "AXValueChanged");
4002 [_bridge respondToChangedContents];
4005 bool KWQKHTMLPart::isContentEditable() const
4007 return KHTMLPart::isContentEditable() || [_bridge isEditable];
4010 bool KWQKHTMLPart::shouldBeginEditing(const Range &range) const
4012 ASSERT(!range.isNull());
4013 return [_bridge shouldBeginEditing:[DOMRange _rangeWithImpl:range.handle()]];
4016 bool KWQKHTMLPart::shouldEndEditing(const Range &range) const
4018 ASSERT(!range.isNull());
4019 return [_bridge shouldEndEditing:[DOMRange _rangeWithImpl:range.handle()]];
4022 DOM::Range KWQKHTMLPart::markedTextRange() const
4024 return m_markedTextRange;
4027 void KWQKHTMLPart::setMarkedTextRange(const DOM::Range &range)
4029 ASSERT(!range.handle() || range.startContainer() == range.endContainer());
4030 ASSERT(!range.handle() || range.collapsed() || range.startContainer().nodeType() == Node::TEXT_NODE);
4032 if (m_markedTextRange.handle() && xmlDocImpl()
4033 && m_markedTextRange.startContainer().handle()->renderer()) {
4034 m_markedTextRange.startContainer().handle()->renderer()->repaint();
4037 if ( range.handle() && range.collapsed() ) {
4038 m_markedTextRange = DOM::Range(0);
4040 m_markedTextRange = range;
4043 if (m_markedTextRange.handle() && xmlDocImpl()
4044 && m_markedTextRange.startContainer().handle()->renderer()) {
4045 m_markedTextRange.startContainer().handle()->renderer()->repaint();
4049 bool KWQKHTMLPart::canGoBackOrForward(int distance) const
4051 return [_bridge canGoBackOrForward:distance];
4054 void KWQKHTMLPart::prepareForUserAction()
4056 // Reset the multiple form submission protection code.
4057 // We'll let you submit the same form twice if you do two separate user actions.
4058 _submittedFormURL = KURL();
4061 void KWQKHTMLPart::didFirstLayout()
4063 [_bridge didFirstLayout];
4066 NSMutableDictionary *KWQKHTMLPart::dashboardRegionsDictionary()
4068 DocumentImpl *doc = xmlDocImpl();
4073 const QValueList<DashboardRegionValue> regions = doc->dashboardRegions();
4074 uint i, count = regions.count();
4076 // Convert the QValueList<DashboardRegionValue> into a NSDictionary of WebDashboardRegions
4077 NSMutableDictionary *webRegions = [[[NSMutableDictionary alloc] initWithCapacity:count] autorelease];
4078 for (i = 0; i < count; i++) {
4079 DashboardRegionValue region = regions[i];
4081 if (region.type == StyleDashboardRegion::None)
4085 clip.origin.x = region.clip.x();
4086 clip.origin.y = region.clip.y();
4087 clip.size.width = region.clip.width();
4088 clip.size.height = region.clip.height();
4090 rect.origin.x = region.bounds.x();
4091 rect.origin.y = region.bounds.y();
4092 rect.size.width = region.bounds.width();
4093 rect.size.height = region.bounds.height();
4094 NSString *label = region.label.getNSString();
4095 WebDashboardRegionType type = WebDashboardRegionTypeNone;
4096 if (region.type == StyleDashboardRegion::Circle)
4097 type = WebDashboardRegionTypeCircle;
4098 else if (region.type == StyleDashboardRegion::Rectangle)
4099 type = WebDashboardRegionTypeRectangle;
4100 NSMutableArray *regionValues = [webRegions objectForKey:label];
4101 if (!regionValues) {
4102 regionValues = [NSMutableArray array];
4103 [webRegions setObject:regionValues forKey:label];
4106 WebDashboardRegion *webRegion = [[[WebDashboardRegion alloc] initWithRect:rect clip:clip type:type] autorelease];
4107 [regionValues addObject:webRegion];