2 * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
3 * 1999 Lars Knoll <knoll@kde.org>
4 * 1999 Antti Koivisto <koivisto@kde.org>
5 * 2000 Simon Hausmann <hausmann@kde.org>
6 * 2000 Stefan Schimanski <1Stein@gmx.de>
7 * 2001 George Staikos <staikos@kde.org>
8 * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
9 * Copyright (C) 2005 Alexey Proskuryakov <ap@nypop.com>
10 * Copyright (C) 2007 Trolltech ASA
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
22 * You should have received a copy of the GNU Library General Public License
23 * along with this library; see the file COPYING.LIB. If not, write to
24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 * Boston, MA 02110-1301, USA.
30 #include "FramePrivate.h"
32 #include "ApplyStyleCommand.h"
33 #include "BeforeUnloadEvent.h"
34 #include "CSSComputedStyleDeclaration.h"
35 #include "CSSProperty.h"
36 #include "CSSPropertyNames.h"
37 #include "CachedCSSStyleSheet.h"
38 #include "DOMWindow.h"
39 #include "DocLoader.h"
40 #include "DocumentType.h"
41 #include "EditingText.h"
42 #include "EditorClient.h"
43 #include "EventNames.h"
44 #include "FocusController.h"
45 #include "FrameLoader.h"
46 #include "FrameView.h"
47 #include "GraphicsContext.h"
48 #include "HitTestResult.h"
49 #include "HTMLDocument.h"
50 #include "HTMLFormElement.h"
51 #include "HTMLFrameElementBase.h"
52 #include "HTMLGenericFormElement.h"
53 #include "HTMLNames.h"
54 #include "HTMLTableCellElement.h"
56 #include "MediaFeatureNames.h"
59 #include "RegularExpression.h"
60 #include "RenderPart.h"
61 #include "RenderTableCell.h"
62 #include "RenderTextControl.h"
63 #include "RenderTheme.h"
64 #include "RenderView.h"
66 #include "SystemTime.h"
67 #include "TextIterator.h"
68 #include "TextResourceDecoder.h"
70 #include "bindings/NP_jsobject.h"
71 #include "bindings/npruntime_impl.h"
72 #include "bindings/runtime_root.h"
73 #include "kjs_proxy.h"
74 #include "kjs_window.h"
75 #include "visible_units.h"
78 #include "SVGDocument.h"
79 #include "SVGDocumentExtensions.h"
81 #include "XLinkNames.h"
90 using namespace EventNames;
91 using namespace HTMLNames;
93 static const double caretBlinkFrequency = 0.5;
95 double Frame::s_currentPaintTimeStamp = 0.0;
97 class UserStyleSheetLoader : public CachedResourceClient {
99 UserStyleSheetLoader(PassRefPtr<Document> document, const String& url)
100 : m_document(document)
101 , m_cachedSheet(m_document->docLoader()->requestUserCSSStyleSheet(url, ""))
103 m_document->addPendingSheet();
104 m_cachedSheet->ref(this);
106 ~UserStyleSheetLoader()
108 if (!m_cachedSheet->isLoaded())
109 m_document->removePendingSheet();
110 m_cachedSheet->deref(this);
113 virtual void setCSSStyleSheet(const String& /*URL*/, const String& /*charset*/, const String& sheet)
115 m_document->removePendingSheet();
116 if (Frame* frame = m_document->frame())
117 frame->setUserStyleSheet(sheet);
119 RefPtr<Document> m_document;
120 CachedCSSStyleSheet* m_cachedSheet;
124 WTFLogChannel LogWebCoreFrameLeaks = { 0x00000000, "", WTFLogChannelOn };
126 struct FrameCounter {
131 LOG(WebCoreFrameLeaks, "LEAK: %d Frame\n", count);
134 int FrameCounter::count = 0;
135 static FrameCounter frameCounter;
138 static inline Frame* parentFromOwnerElement(HTMLFrameOwnerElement* ownerElement)
142 return ownerElement->document()->frame();
145 Frame::Frame(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* frameLoaderClient)
146 : d(new FramePrivate(page, parentFromOwnerElement(ownerElement), this, ownerElement, frameLoaderClient))
148 AtomicString::init();
151 QualifiedName::init();
152 MediaFeatureNames::init();
162 page->setMainFrame(this);
164 // FIXME: Frames were originally created with a refcount of 1.
165 // Leave this ref call here until we can straighten that out.
167 page->incrementFrameCount();
168 ownerElement->m_contentFrame = this;
172 ++FrameCounter::count;
179 loader()->clearRecordedFormValues();
185 loader()->cancelAndClear();
187 // FIXME: We should not be doing all this work inside the destructor
189 ASSERT(!d->m_lifeSupportTimer.isActive());
192 --FrameCounter::count;
195 if (d->m_jscript && d->m_jscript->haveInterpreter())
196 static_cast<KJS::Window*>(d->m_jscript->interpreter()->globalObject())->disconnectFrame();
198 disconnectOwnerElement();
201 d->m_domWindow->disconnectFrame();
205 d->m_view->clearFrame();
208 ASSERT(!d->m_lifeSupportTimer.isActive());
210 delete d->m_userStyleSheetLoader;
220 FrameLoader* Frame::loader() const
225 FrameView* Frame::view() const
227 return d->m_view.get();
230 void Frame::setView(FrameView* view)
232 // Detach the document now, so any onUnload handlers get run - if
233 // we wait until the view is destroyed, then things won't be
234 // hooked up enough for some JavaScript calls to work.
235 if (!view && d->m_doc && d->m_doc->attached() && !d->m_doc->inPageCache()) {
236 // FIXME: We don't call willRemove here. Why is that OK?
239 d->m_view->unscheduleRelayout();
241 eventHandler()->clear();
245 // Only one form submission is allowed per view of a part.
246 // Since this part may be getting reused as a result of being
247 // pulled from the back/forward cache, reset this flag.
248 loader()->resetMultipleFormSubmissionProtection();
251 KJSProxy *Frame::scriptProxy()
253 Settings* settings = this->settings();
254 if (!settings || !settings->isJavaScriptEnabled())
258 d->m_jscript = new KJSProxy(this);
263 Document *Frame::document() const
266 return d->m_doc.get();
270 void Frame::setDocument(PassRefPtr<Document> newDoc)
272 if (d->m_doc && d->m_doc->attached() && !d->m_doc->inPageCache()) {
273 // FIXME: We don't call willRemove here. Why is that OK?
278 if (d->m_doc && d->m_isActive)
279 setUseSecureKeyboardEntry(d->m_doc->useSecureKeyboardEntryWhenActive());
281 if (d->m_doc && !d->m_doc->attached())
284 // Remove the cached 'document' property, which is now stale.
286 d->m_jscript->clearDocumentWrapper();
289 Settings* Frame::settings() const
291 return d->m_page ? d->m_page->settings() : 0;
294 void Frame::setUserStyleSheetLocation(const KURL& url)
296 delete d->m_userStyleSheetLoader;
297 d->m_userStyleSheetLoader = 0;
298 if (d->m_doc && d->m_doc->docLoader())
299 d->m_userStyleSheetLoader = new UserStyleSheetLoader(d->m_doc, url.url());
302 void Frame::setUserStyleSheet(const String& styleSheet)
304 delete d->m_userStyleSheetLoader;
305 d->m_userStyleSheetLoader = 0;
307 d->m_doc->setUserStyleSheet(styleSheet);
310 String Frame::selectedText() const
312 return plainText(selectionController()->toRange().get());
315 IntRect Frame::firstRectForRange(Range* range) const
317 int extraWidthToEndOfLine = 0;
318 ExceptionCode ec = 0;
319 ASSERT(range->startContainer(ec));
320 ASSERT(range->endContainer(ec));
321 IntRect startCaretRect = range->startContainer(ec)->renderer()->caretRect(range->startOffset(ec), DOWNSTREAM, &extraWidthToEndOfLine);
323 IntRect endCaretRect = range->endContainer(ec)->renderer()->caretRect(range->endOffset(ec), UPSTREAM);
326 if (startCaretRect.y() == endCaretRect.y()) {
327 // start and end are on the same line
328 return IntRect(min(startCaretRect.x(), endCaretRect.x()),
330 abs(endCaretRect.x() - startCaretRect.x()),
331 max(startCaretRect.height(), endCaretRect.height()));
334 // start and end aren't on the same line, so go from start to the end of its line
335 return IntRect(startCaretRect.x(),
337 startCaretRect.width() + extraWidthToEndOfLine,
338 startCaretRect.height());
341 SelectionController* Frame::selectionController() const
343 return &d->m_selectionController;
346 Editor* Frame::editor() const
351 TextGranularity Frame::selectionGranularity() const
353 return d->m_selectionGranularity;
356 void Frame::setSelectionGranularity(TextGranularity granularity) const
358 d->m_selectionGranularity = granularity;
361 SelectionController* Frame::dragCaretController() const
363 return d->m_page->dragCaretController();
367 // Either get cached regexp or build one that matches any of the labels.
368 // The regexp we build is of the form: (STR1|STR2|STRN)
369 static RegularExpression *regExpForLabels(const Vector<String>& labels)
371 // REVIEW- version of this call in FrameMac.mm caches based on the NSArray ptrs being
372 // the same across calls. We can't do that.
374 static RegularExpression wordRegExp = RegularExpression("\\w");
375 DeprecatedString pattern("(");
376 unsigned int numLabels = labels.size();
378 for (i = 0; i < numLabels; i++) {
379 DeprecatedString label = labels[i].deprecatedString();
381 bool startsWithWordChar = false;
382 bool endsWithWordChar = false;
383 if (label.length() != 0) {
384 startsWithWordChar = wordRegExp.search(label.at(0)) >= 0;
385 endsWithWordChar = wordRegExp.search(label.at(label.length() - 1)) >= 0;
390 // Search for word boundaries only if label starts/ends with "word characters".
391 // If we always searched for word boundaries, this wouldn't work for languages
393 if (startsWithWordChar) {
394 pattern.append("\\b");
396 pattern.append(label);
397 if (endsWithWordChar) {
398 pattern.append("\\b");
402 return new RegularExpression(pattern, false);
405 String Frame::searchForLabelsAboveCell(RegularExpression* regExp, HTMLTableCellElement* cell)
407 RenderTableCell* cellRenderer = static_cast<RenderTableCell*>(cell->renderer());
409 if (cellRenderer && cellRenderer->isTableCell()) {
410 RenderTableCell* cellAboveRenderer = cellRenderer->table()->cellAbove(cellRenderer);
412 if (cellAboveRenderer) {
413 HTMLTableCellElement* aboveCell =
414 static_cast<HTMLTableCellElement*>(cellAboveRenderer->element());
417 // search within the above cell we found for a match
418 for (Node* n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) {
419 if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
420 // For each text chunk, run the regexp
421 DeprecatedString nodeString = n->nodeValue().deprecatedString();
422 int pos = regExp->searchRev(nodeString);
424 return nodeString.mid(pos, regExp->matchedLength());
430 // Any reason in practice to search all cells in that are above cell?
434 String Frame::searchForLabelsBeforeElement(const Vector<String>& labels, Element* element)
436 RegularExpression* regExp = regExpForLabels(labels);
437 // We stop searching after we've seen this many chars
438 const unsigned int charsSearchedThreshold = 500;
439 // This is the absolute max we search. We allow a little more slop than
440 // charsSearchedThreshold, to make it more likely that we'll search whole nodes.
441 const unsigned int maxCharsSearched = 600;
442 // If the starting element is within a table, the cell that contains it
443 HTMLTableCellElement* startingTableCell = 0;
444 bool searchedCellAbove = false;
446 // walk backwards in the node tree, until another element, or form, or end of tree
447 int unsigned lengthSearched = 0;
449 for (n = element->traversePreviousNode();
450 n && lengthSearched < charsSearchedThreshold;
451 n = n->traversePreviousNode())
453 if (n->hasTagName(formTag)
454 || (n->isHTMLElement()
455 && static_cast<HTMLElement*>(n)->isGenericFormElement()))
457 // We hit another form element or the start of the form - bail out
459 } else if (n->hasTagName(tdTag) && !startingTableCell) {
460 startingTableCell = static_cast<HTMLTableCellElement*>(n);
461 } else if (n->hasTagName(trTag) && startingTableCell) {
462 String result = searchForLabelsAboveCell(regExp, startingTableCell);
463 if (!result.isEmpty())
465 searchedCellAbove = true;
466 } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
467 // For each text chunk, run the regexp
468 DeprecatedString nodeString = n->nodeValue().deprecatedString();
469 // add 100 for slop, to make it more likely that we'll search whole nodes
470 if (lengthSearched + nodeString.length() > maxCharsSearched)
471 nodeString = nodeString.right(charsSearchedThreshold - lengthSearched);
472 int pos = regExp->searchRev(nodeString);
474 return nodeString.mid(pos, regExp->matchedLength());
476 lengthSearched += nodeString.length();
480 // If we started in a cell, but bailed because we found the start of the form or the
481 // previous element, we still might need to search the row above us for a label.
482 if (startingTableCell && !searchedCellAbove) {
483 return searchForLabelsAboveCell(regExp, startingTableCell);
488 String Frame::matchLabelsAgainstElement(const Vector<String>& labels, Element* element)
490 DeprecatedString name = element->getAttribute(nameAttr).deprecatedString();
491 // Make numbers and _'s in field names behave like word boundaries, e.g., "address2"
492 name.replace(RegularExpression("[[:digit:]]"), " ");
493 name.replace('_', ' ');
495 RegularExpression* regExp = regExpForLabels(labels);
496 // Use the largest match we can find in the whole name string
503 pos = regExp->search(name, start);
505 length = regExp->matchedLength();
506 if (length >= bestLength) {
515 return name.mid(bestPos, bestLength);
519 const Selection& Frame::mark() const
524 void Frame::setMark(const Selection& s)
526 ASSERT(!s.base().node() || s.base().node()->document() == document());
527 ASSERT(!s.extent().node() || s.extent().node()->document() == document());
528 ASSERT(!s.start().node() || s.start().node()->document() == document());
529 ASSERT(!s.end().node() || s.end().node()->document() == document());
534 void Frame::notifyRendererOfSelectionChange(bool userTriggered)
536 RenderObject* renderer = 0;
537 if (selectionController()->rootEditableElement())
538 renderer = selectionController()->rootEditableElement()->shadowAncestorNode()->renderer();
540 // If the current selection is in a textfield or textarea, notify the renderer that the selection has changed
541 if (renderer && (renderer->isTextArea() || renderer->isTextField()))
542 static_cast<RenderTextControl*>(renderer)->selectionChanged(userTriggered);
545 void Frame::invalidateSelection()
547 selectionController()->setNeedsLayout();
548 selectionLayoutChanged();
551 void Frame::setCaretVisible(bool flag)
553 if (d->m_caretVisible == flag)
555 clearCaretRectIfNeeded();
556 d->m_caretVisible = flag;
557 selectionLayoutChanged();
560 void Frame::clearCaretRectIfNeeded()
562 if (d->m_caretPaint) {
563 d->m_caretPaint = false;
564 selectionController()->invalidateCaretRect();
568 // Helper function that tells whether a particular node is an element that has an entire
569 // Frame and FrameView, a <frame>, <iframe>, or <object>.
570 static bool isFrameElement(const Node *n)
574 RenderObject *renderer = n->renderer();
575 if (!renderer || !renderer->isWidget())
577 Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
578 return widget && widget->isFrameView();
581 void Frame::setFocusedNodeIfNeeded()
583 if (!document() || selectionController()->isNone() || !d->m_isActive)
586 Node* target = selectionController()->rootEditableElement();
588 RenderObject* renderer = target->renderer();
590 // Walk up the render tree to search for a node to focus.
591 // Walking up the DOM tree wouldn't work for shadow trees, like those behind the engine-based text fields.
593 // We don't want to set focus on a subframe when selecting in a parent frame,
594 // so add the !isFrameElement check here. There's probably a better way to make this
595 // work in the long term, but this is the safest fix at this time.
596 if (target && target->isMouseFocusable() && !isFrameElement(target)) {
597 page()->focusController()->setFocusedNode(target, this);
600 renderer = renderer->parent();
602 target = renderer->element();
604 document()->setFocusedNode(0);
608 void Frame::selectionLayoutChanged()
610 bool caretRectChanged = selectionController()->recomputeCaretRect();
612 bool shouldBlink = d->m_caretVisible
613 && selectionController()->isCaret() && selectionController()->isContentEditable();
615 // If the caret moved, stop the blink timer so we can restart with a
616 // black caret in the new location.
617 if (caretRectChanged || !shouldBlink)
618 d->m_caretBlinkTimer.stop();
620 // Start blinking with a black caret. Be sure not to restart if we're
621 // already blinking in the right location.
622 if (shouldBlink && !d->m_caretBlinkTimer.isActive()) {
623 d->m_caretBlinkTimer.startRepeating(caretBlinkFrequency);
624 if (!d->m_caretPaint) {
625 d->m_caretPaint = true;
626 selectionController()->invalidateCaretRect();
631 d->m_doc->updateSelection();
634 void Frame::caretBlinkTimerFired(Timer<Frame>*)
636 ASSERT(d->m_caretVisible);
637 ASSERT(selectionController()->isCaret());
638 bool caretPaint = d->m_caretPaint;
639 if (selectionController()->isCaretBlinkingSuspended() && caretPaint)
641 d->m_caretPaint = !caretPaint;
642 selectionController()->invalidateCaretRect();
645 void Frame::paintCaret(GraphicsContext* p, const IntRect& rect) const
647 if (d->m_caretPaint && d->m_caretVisible)
648 selectionController()->paintCaret(p, rect);
651 void Frame::paintDragCaret(GraphicsContext* p, const IntRect& rect) const
653 SelectionController* dragCaretController = d->m_page->dragCaretController();
654 ASSERT(dragCaretController->selection().isCaret());
655 if (dragCaretController->selection().start().node()->document()->frame() == this)
656 dragCaretController->paintCaret(p, rect);
659 int Frame::zoomFactor() const
661 return d->m_zoomFactor;
664 void Frame::setZoomFactor(int percent)
666 if (d->m_zoomFactor == percent)
670 if (d->m_doc && d->m_doc->isSVGDocument()) {
671 if (!static_cast<SVGDocument*>(d->m_doc.get())->zoomAndPanEnabled())
673 d->m_zoomFactor = percent;
674 if (d->m_doc->renderer())
675 d->m_doc->renderer()->repaint();
679 d->m_zoomFactor = percent;
681 d->m_doc->recalcStyle(Node::Force);
683 for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling())
684 child->setZoomFactor(d->m_zoomFactor);
686 if (d->m_doc && d->m_doc->renderer() && d->m_doc->renderer()->needsLayout())
690 void Frame::setPrinting(bool printing, float minPageWidth, float maxPageWidth, bool adjustViewSize)
695 d->m_doc->setPrinting(printing);
696 view()->setMediaType(printing ? "print" : "screen");
697 d->m_doc->updateStyleSelector();
698 forceLayoutWithPageWidthRange(minPageWidth, maxPageWidth, adjustViewSize);
700 for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling())
701 child->setPrinting(printing, minPageWidth, maxPageWidth, adjustViewSize);
704 void Frame::setJSStatusBarText(const String& text)
706 d->m_kjsStatusBarText = text;
708 d->m_page->chrome()->setStatusbarText(this, d->m_kjsStatusBarText);
711 void Frame::setJSDefaultStatusBarText(const String& text)
713 d->m_kjsDefaultStatusBarText = text;
715 d->m_page->chrome()->setStatusbarText(this, d->m_kjsDefaultStatusBarText);
718 String Frame::jsStatusBarText() const
720 return d->m_kjsStatusBarText;
723 String Frame::jsDefaultStatusBarText() const
725 return d->m_kjsDefaultStatusBarText;
728 void Frame::reparseConfiguration()
731 d->m_doc->docLoader()->setAutoLoadImages(d->m_page && d->m_page->settings()->loadsImagesAutomatically());
733 const KURL userStyleSheetLocation = d->m_page ? d->m_page->settings()->userStyleSheetLocation() : KURL();
734 if (!userStyleSheetLocation.isEmpty())
735 setUserStyleSheetLocation(userStyleSheetLocation);
737 setUserStyleSheet(String());
739 // FIXME: It's not entirely clear why the following is needed.
740 // The document automatically does this as required when you set the style sheet.
741 // But we had problems when this code was removed. Details are in
742 // <http://bugs.webkit.org/show_bug.cgi?id=8079>.
744 d->m_doc->updateStyleSelector();
747 bool Frame::shouldChangeSelection(const Selection& newSelection) const
749 return shouldChangeSelection(selectionController()->selection(), newSelection, newSelection.affinity(), false);
752 bool Frame::shouldChangeSelection(const Selection& oldSelection, const Selection& newSelection, EAffinity affinity, bool stillSelecting) const
754 return editor()->client()->shouldChangeSelectedRange(oldSelection.toRange().get(), newSelection.toRange().get(),
755 affinity, stillSelecting);
758 bool Frame::shouldDeleteSelection(const Selection& selection) const
760 return editor()->client()->shouldDeleteRange(selection.toRange().get());
763 bool Frame::isContentEditable() const
765 if (d->m_editor.clientIsEditable())
769 return d->m_doc->inDesignMode();
774 void Frame::setUseSecureKeyboardEntry(bool)
780 void Frame::updateSecureKeyboardEntryIfActive()
783 setUseSecureKeyboardEntry(d->m_doc->useSecureKeyboardEntryWhenActive());
786 CSSMutableStyleDeclaration *Frame::typingStyle() const
788 return d->m_typingStyle.get();
791 void Frame::setTypingStyle(CSSMutableStyleDeclaration *style)
793 d->m_typingStyle = style;
796 void Frame::clearTypingStyle()
798 d->m_typingStyle = 0;
801 void Frame::transpose()
803 issueTransposeCommand();
806 void Frame::computeAndSetTypingStyle(CSSStyleDeclaration *style, EditAction editingAction)
808 if (!style || style->length() == 0) {
813 // Calculate the current typing style.
814 RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable();
816 typingStyle()->merge(mutableStyle.get());
817 mutableStyle = typingStyle();
820 Node *node = selectionController()->selection().visibleStart().deepEquivalent().node();
821 CSSComputedStyleDeclaration computedStyle(node);
822 computedStyle.diff(mutableStyle.get());
824 // Handle block styles, substracting these from the typing style.
825 RefPtr<CSSMutableStyleDeclaration> blockStyle = mutableStyle->copyBlockProperties();
826 blockStyle->diff(mutableStyle.get());
827 if (document() && blockStyle->length() > 0)
828 applyCommand(new ApplyStyleCommand(document(), blockStyle.get(), editingAction));
830 // Set the remaining style as the typing style.
831 d->m_typingStyle = mutableStyle.release();
834 static void updateState(CSSMutableStyleDeclaration *desiredStyle, CSSComputedStyleDeclaration *computedStyle, bool& atStart, Frame::TriState& state)
836 DeprecatedValueListConstIterator<CSSProperty> end;
837 for (DeprecatedValueListConstIterator<CSSProperty> it = desiredStyle->valuesIterator(); it != end; ++it) {
838 int propertyID = (*it).id();
839 String desiredProperty = desiredStyle->getPropertyValue(propertyID);
840 String computedProperty = computedStyle->getPropertyValue(propertyID);
841 Frame::TriState propertyState = equalIgnoringCase(desiredProperty, computedProperty)
842 ? Frame::trueTriState : Frame::falseTriState;
844 state = propertyState;
846 } else if (state != propertyState) {
847 state = Frame::mixedTriState;
853 Frame::TriState Frame::selectionHasStyle(CSSStyleDeclaration *style) const
856 TriState state = falseTriState;
858 RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable();
860 if (!selectionController()->isRange()) {
862 RefPtr<CSSComputedStyleDeclaration> selectionStyle = selectionComputedStyle(nodeToRemove);
864 return falseTriState;
865 updateState(mutableStyle.get(), selectionStyle.get(), atStart, state);
867 ExceptionCode ec = 0;
868 nodeToRemove->remove(ec);
872 for (Node* node = selectionController()->start().node(); node; node = node->traverseNextNode()) {
873 RefPtr<CSSComputedStyleDeclaration> computedStyle = new CSSComputedStyleDeclaration(node);
875 updateState(mutableStyle.get(), computedStyle.get(), atStart, state);
876 if (state == mixedTriState)
878 if (node == selectionController()->end().node())
886 String Frame::selectionStartStylePropertyValue(int stylePropertyID) const
889 RefPtr<CSSStyleDeclaration> selectionStyle = selectionComputedStyle(nodeToRemove);
893 String value = selectionStyle->getPropertyValue(stylePropertyID);
896 ExceptionCode ec = 0;
897 nodeToRemove->remove(ec);
904 CSSComputedStyleDeclaration *Frame::selectionComputedStyle(Node *&nodeToRemove) const
911 if (selectionController()->isNone())
914 RefPtr<Range> range(selectionController()->toRange());
915 Position pos = range->editingStartPosition();
917 Element *elem = pos.element();
921 RefPtr<Element> styleElement = elem;
922 ExceptionCode ec = 0;
924 if (d->m_typingStyle) {
925 styleElement = document()->createElementNS(xhtmlNamespaceURI, "span", ec);
928 styleElement->setAttribute(styleAttr, d->m_typingStyle->cssText().impl(), ec);
931 styleElement->appendChild(document()->createEditingTextNode(""), ec);
934 if (elem->renderer() && elem->renderer()->canHaveChildren()) {
935 elem->appendChild(styleElement, ec);
937 Node *parent = elem->parent();
938 Node *next = elem->nextSibling();
941 parent->insertBefore(styleElement, next, ec);
943 parent->appendChild(styleElement, ec);
948 nodeToRemove = styleElement.get();
951 return new CSSComputedStyleDeclaration(styleElement);
954 void Frame::textFieldDidBeginEditing(Element* e)
956 if (editor()->client())
957 editor()->client()->textFieldDidBeginEditing(e);
960 void Frame::textFieldDidEndEditing(Element* e)
962 if (editor()->client())
963 editor()->client()->textFieldDidEndEditing(e);
966 void Frame::textDidChangeInTextField(Element* e)
968 if (editor()->client())
969 editor()->client()->textDidChangeInTextField(e);
972 bool Frame::doTextFieldCommandFromEvent(Element* e, KeyboardEvent* ke)
974 if (editor()->client())
975 return editor()->client()->doTextFieldCommandFromEvent(e, ke);
980 void Frame::textWillBeDeletedInTextField(Element* input)
982 if (editor()->client())
983 editor()->client()->textWillBeDeletedInTextField(input);
986 void Frame::textDidChangeInTextArea(Element* e)
988 if (editor()->client())
989 editor()->client()->textDidChangeInTextArea(e);
992 void Frame::applyEditingStyleToBodyElement() const
997 RefPtr<NodeList> list = d->m_doc->getElementsByTagName("body");
998 unsigned len = list->length();
999 for (unsigned i = 0; i < len; i++) {
1000 applyEditingStyleToElement(static_cast<Element*>(list->item(i)));
1004 void Frame::removeEditingStyleFromBodyElement() const
1009 RefPtr<NodeList> list = d->m_doc->getElementsByTagName("body");
1010 unsigned len = list->length();
1011 for (unsigned i = 0; i < len; i++) {
1012 removeEditingStyleFromElement(static_cast<Element*>(list->item(i)));
1016 void Frame::applyEditingStyleToElement(Element* element) const
1021 CSSStyleDeclaration* style = element->style();
1024 ExceptionCode ec = 0;
1025 style->setProperty(CSS_PROP_WORD_WRAP, "break-word", false, ec);
1027 style->setProperty(CSS_PROP__WEBKIT_NBSP_MODE, "space", false, ec);
1029 style->setProperty(CSS_PROP__WEBKIT_LINE_BREAK, "after-white-space", false, ec);
1033 void Frame::removeEditingStyleFromElement(Element*) const
1038 static HashSet<Frame*>& keepAliveSet()
1040 static HashSet<Frame*> staticKeepAliveSet;
1041 return staticKeepAliveSet;
1045 void Frame::keepAlive()
1047 if (d->m_lifeSupportTimer.isActive())
1050 keepAliveSet().add(this);
1053 d->m_lifeSupportTimer.startOneShot(0);
1057 void Frame::cancelAllKeepAlive()
1059 HashSet<Frame*>::iterator end = keepAliveSet().end();
1060 for (HashSet<Frame*>::iterator it = keepAliveSet().begin(); it != end; ++it) {
1062 frame->d->m_lifeSupportTimer.stop();
1065 keepAliveSet().clear();
1069 void Frame::lifeSupportTimerFired(Timer<Frame>*)
1072 keepAliveSet().remove(this);
1077 KJS::Bindings::RootObject* Frame::bindingRootObject()
1079 Settings* settings = this->settings();
1080 if (!settings || !settings->isJavaScriptEnabled())
1083 if (!d->m_bindingRootObject) {
1085 d->m_bindingRootObject = KJS::Bindings::RootObject::create(0, scriptProxy()->interpreter());
1087 return d->m_bindingRootObject.get();
1090 PassRefPtr<KJS::Bindings::RootObject> Frame::createRootObject(void* nativeHandle, PassRefPtr<KJS::Interpreter> interpreter)
1092 RootObjectMap::iterator it = d->m_rootObjects.find(nativeHandle);
1093 if (it != d->m_rootObjects.end())
1096 RefPtr<KJS::Bindings::RootObject> rootObject = KJS::Bindings::RootObject::create(nativeHandle, interpreter);
1098 d->m_rootObjects.set(nativeHandle, rootObject);
1099 return rootObject.release();
1103 NPObject* Frame::windowScriptNPObject()
1105 if (!d->m_windowScriptNPObject) {
1106 Settings* settings = this->settings();
1107 if (settings && settings->isJavaScriptEnabled()) {
1108 // JavaScript is enabled, so there is a JavaScript window object. Return an NPObject bound to the window
1111 KJS::JSObject* win = KJS::Window::retrieveWindow(this);
1113 KJS::Bindings::RootObject* root = bindingRootObject();
1114 d->m_windowScriptNPObject = _NPN_CreateScriptObject(0, win, root, root);
1116 // JavaScript is not enabled, so we cannot bind the NPObject to the JavaScript window object.
1117 // Instead, we create an NPObject of a different class, one which is not bound to a JavaScript object.
1118 d->m_windowScriptNPObject = _NPN_CreateNoScriptObject();
1122 return d->m_windowScriptNPObject;
1126 void Frame::clearScriptProxy()
1129 d->m_jscript->clear();
1132 void Frame::clearDOMWindow()
1135 d->m_domWindow->clear();
1138 void Frame::cleanupScriptObjectsForPlugin(void* nativeHandle)
1140 RootObjectMap::iterator it = d->m_rootObjects.find(nativeHandle);
1142 if (it == d->m_rootObjects.end())
1145 it->second->invalidate();
1146 d->m_rootObjects.remove(it);
1149 void Frame::clearScriptObjects()
1153 RootObjectMap::const_iterator end = d->m_rootObjects.end();
1154 for (RootObjectMap::const_iterator it = d->m_rootObjects.begin(); it != end; ++it)
1155 it->second->invalidate();
1157 d->m_rootObjects.clear();
1159 if (d->m_bindingRootObject) {
1160 d->m_bindingRootObject->invalidate();
1161 d->m_bindingRootObject = 0;
1165 if (d->m_windowScriptNPObject) {
1166 // Call _NPN_DeallocateObject() instead of _NPN_ReleaseObject() so that we don't leak if a plugin fails to release the window
1167 // script object properly.
1168 // This shouldn't cause any problems for plugins since they should have already been stopped and destroyed at this point.
1169 _NPN_DeallocateObject(d->m_windowScriptNPObject);
1170 d->m_windowScriptNPObject = 0;
1174 clearPlatformScriptObjects();
1177 RenderObject *Frame::renderer() const
1179 Document *doc = document();
1180 return doc ? doc->renderer() : 0;
1183 HTMLFrameOwnerElement* Frame::ownerElement() const
1185 return d->m_ownerElement;
1188 RenderPart* Frame::ownerRenderer()
1190 HTMLFrameOwnerElement* ownerElement = d->m_ownerElement;
1193 return static_cast<RenderPart*>(ownerElement->renderer());
1196 // returns FloatRect because going through IntRect would truncate any floats
1197 FloatRect Frame::selectionRect(bool clipToVisibleContent) const
1199 RenderView *root = static_cast<RenderView*>(renderer());
1203 IntRect selectionRect = root->selectionRect(clipToVisibleContent);
1204 return clipToVisibleContent ? intersection(selectionRect, d->m_view->visibleContentRect()) : selectionRect;
1207 void Frame::selectionTextRects(Vector<FloatRect>& rects, bool clipToVisibleContent) const
1209 RenderView *root = static_cast<RenderView*>(renderer());
1213 RefPtr<Range> selectedRange = selectionController()->toRange();
1215 Vector<IntRect> intRects;
1216 selectedRange->addLineBoxRects(intRects, true);
1218 unsigned size = intRects.size();
1219 FloatRect visibleContentRect = d->m_view->visibleContentRect();
1220 for (unsigned i = 0; i < size; ++i)
1221 if (clipToVisibleContent)
1222 rects.append(intersection(intRects[i], visibleContentRect));
1224 rects.append(intRects[i]);
1228 bool Frame::isFrameSet() const
1230 Document* document = d->m_doc.get();
1231 if (!document || !document->isHTMLDocument())
1233 Node *body = static_cast<HTMLDocument*>(document)->body();
1234 return body && body->renderer() && body->hasTagName(framesetTag);
1237 // Scans logically forward from "start", including any child frames
1238 static HTMLFormElement *scanForForm(Node *start)
1241 for (n = start; n; n = n->traverseNextNode()) {
1242 if (n->hasTagName(formTag))
1243 return static_cast<HTMLFormElement*>(n);
1244 else if (n->isHTMLElement() && static_cast<HTMLElement*>(n)->isGenericFormElement())
1245 return static_cast<HTMLGenericFormElement*>(n)->form();
1246 else if (n->hasTagName(frameTag) || n->hasTagName(iframeTag)) {
1247 Node *childDoc = static_cast<HTMLFrameElementBase*>(n)->contentDocument();
1248 if (HTMLFormElement *frameResult = scanForForm(childDoc))
1255 // We look for either the form containing the current focus, or for one immediately after it
1256 HTMLFormElement *Frame::currentForm() const
1258 // start looking either at the active (first responder) node, or where the selection is
1259 Node *start = d->m_doc ? d->m_doc->focusedNode() : 0;
1261 start = selectionController()->start().node();
1263 // try walking up the node tree to find a form element
1265 for (n = start; n; n = n->parentNode()) {
1266 if (n->hasTagName(formTag))
1267 return static_cast<HTMLFormElement*>(n);
1268 else if (n->isHTMLElement()
1269 && static_cast<HTMLElement*>(n)->isGenericFormElement())
1270 return static_cast<HTMLGenericFormElement*>(n)->form();
1273 // try walking forward in the node tree to find a form element
1274 return start ? scanForForm(start) : 0;
1277 // FIXME: should this go in SelectionController?
1278 void Frame::revealSelection(const RenderLayer::ScrollAlignment& alignment) const
1282 switch (selectionController()->state()) {
1283 case Selection::NONE:
1286 case Selection::CARET:
1287 rect = selectionController()->caretRect();
1290 case Selection::RANGE:
1291 rect = enclosingIntRect(selectionRect(false));
1295 Position start = selectionController()->start();
1297 ASSERT(start.node());
1298 if (start.node() && start.node()->renderer()) {
1299 // FIXME: This code only handles scrolling the startContainer's layer, but
1300 // the selection rect could intersect more than just that.
1301 // See <rdar://problem/4799899>.
1302 if (RenderLayer *layer = start.node()->renderer()->enclosingLayer())
1303 layer->scrollRectToVisible(rect, alignment, alignment);
1307 void Frame::revealCaret(const RenderLayer::ScrollAlignment& alignment) const
1309 if (selectionController()->isNone())
1312 Position extent = selectionController()->extent();
1313 if (extent.node() && extent.node()->renderer()) {
1314 IntRect extentRect = VisiblePosition(extent).caretRect();
1315 RenderLayer* layer = extent.node()->renderer()->enclosingLayer();
1317 layer->scrollRectToVisible(extentRect, alignment, alignment);
1321 // FIXME: why is this here instead of on the FrameView?
1322 void Frame::paint(GraphicsContext* p, const IntRect& rect)
1326 if (!document() || document()->printing())
1327 fillWithRed = false; // Printing, don't fill with red (can't remember why).
1328 else if (document()->ownerElement())
1329 fillWithRed = false; // Subframe, don't fill with red.
1330 else if (view() && view()->isTransparent())
1331 fillWithRed = false; // Transparent, don't fill with red.
1332 else if (d->m_paintRestriction == PaintRestrictionSelectionOnly || d->m_paintRestriction == PaintRestrictionSelectionOnlyBlackText)
1333 fillWithRed = false; // Selections are transparent, don't fill with red.
1334 else if (d->m_elementToDraw)
1335 fillWithRed = false; // Element images are transparent, don't fill with red.
1340 p->fillRect(rect, Color(0xFF, 0, 0));
1343 bool isTopLevelPainter = !s_currentPaintTimeStamp;
1344 if (isTopLevelPainter)
1345 s_currentPaintTimeStamp = currentTime();
1348 ASSERT(d->m_view && !d->m_view->needsLayout());
1349 ASSERT(!d->m_isPainting);
1351 d->m_isPainting = true;
1353 // d->m_elementToDraw is used to draw only one element
1354 RenderObject *eltRenderer = d->m_elementToDraw ? d->m_elementToDraw->renderer() : 0;
1355 if (d->m_paintRestriction == PaintRestrictionNone)
1356 renderer()->document()->invalidateRenderedRectsForMarkersInRect(rect);
1357 renderer()->layer()->paint(p, rect, d->m_paintRestriction, eltRenderer);
1359 d->m_isPainting = false;
1361 // Regions may have changed as a result of the visibility/z-index of element changing.
1362 if (renderer()->document()->dashboardRegionsDirty())
1363 renderer()->view()->frameView()->updateDashboardRegions();
1365 LOG_ERROR("called Frame::paint with nil renderer");
1367 if (isTopLevelPainter)
1368 s_currentPaintTimeStamp = 0;
1371 void Frame::setPaintRestriction(PaintRestriction pr)
1373 d->m_paintRestriction = pr;
1376 bool Frame::isPainting() const
1378 return d->m_isPainting;
1381 void Frame::adjustPageHeight(float *newBottom, float oldTop, float oldBottom, float bottomLimit)
1383 RenderView *root = static_cast<RenderView*>(document()->renderer());
1385 // Use a context with painting disabled.
1386 GraphicsContext context((PlatformGraphicsContext*)0);
1387 root->setTruncatedAt((int)floorf(oldBottom));
1388 IntRect dirtyRect(0, (int)floorf(oldTop), root->docWidth(), (int)ceilf(oldBottom - oldTop));
1389 root->layer()->paint(&context, dirtyRect);
1390 *newBottom = root->bestTruncatedAt();
1391 if (*newBottom == 0)
1392 *newBottom = oldBottom;
1394 *newBottom = oldBottom;
1397 Frame* Frame::frameForWidget(const Widget* widget)
1399 ASSERT_ARG(widget, widget);
1401 if (RenderWidget* renderer = RenderWidget::find(widget))
1402 if (Node* node = renderer->node())
1403 return node->document()->frame();
1405 // Assume all widgets are either a FrameView or owned by a RenderWidget.
1406 // FIXME: That assumption is not right for scroll bars!
1407 ASSERT(widget->isFrameView());
1408 return static_cast<const FrameView*>(widget)->frame();
1411 void Frame::forceLayout(bool allowSubtree)
1413 FrameView *v = d->m_view.get();
1415 v->layout(allowSubtree);
1416 // We cannot unschedule a pending relayout, since the force can be called with
1417 // a tiny rectangle from a drawRect update. By unscheduling we in effect
1418 // "validate" and stop the necessary full repaint from occurring. Basically any basic
1419 // append/remove DHTML is broken by this call. For now, I have removed the optimization
1420 // until we have a better invalidation stategy. -dwh
1421 //v->unscheduleRelayout();
1425 void Frame::forceLayoutWithPageWidthRange(float minPageWidth, float maxPageWidth, bool adjustViewSize)
1427 // Dumping externalRepresentation(m_frame->renderer()).ascii() is a good trick to see
1428 // the state of things before and after the layout
1429 RenderView *root = static_cast<RenderView*>(document()->renderer());
1431 // This magic is basically copied from khtmlview::print
1432 int pageW = (int)ceilf(minPageWidth);
1433 root->setWidth(pageW);
1434 root->setNeedsLayoutAndPrefWidthsRecalc();
1437 // If we don't fit in the minimum page width, we'll lay out again. If we don't fit in the
1438 // maximum page width, we will lay out to the maximum page width and clip extra content.
1439 // FIXME: We are assuming a shrink-to-fit printing implementation. A cropping
1440 // implementation should not do this!
1441 int rightmostPos = root->rightmostPosition();
1442 if (rightmostPos > minPageWidth) {
1443 pageW = min(rightmostPos, (int)ceilf(maxPageWidth));
1444 root->setWidth(pageW);
1445 root->setNeedsLayoutAndPrefWidthsRecalc();
1450 if (adjustViewSize && view())
1451 view()->adjustViewSize();
1454 void Frame::sendResizeEvent()
1456 if (Document* doc = document())
1457 doc->dispatchWindowEvent(EventNames::resizeEvent, false, false);
1460 void Frame::sendScrollEvent()
1462 FrameView* v = d->m_view.get();
1465 v->setWasScrolledByUser(true);
1466 Document* doc = document();
1469 doc->dispatchHTMLEvent(scrollEvent, true, false);
1472 void Frame::clearTimers(FrameView *view)
1475 view->unscheduleRelayout();
1476 if (view->frame()) {
1477 Document* document = view->frame()->document();
1478 if (document && document->renderer() && document->renderer()->hasLayer())
1479 document->renderer()->layer()->suspendMarquees();
1484 void Frame::clearTimers()
1486 clearTimers(d->m_view.get());
1489 RenderStyle *Frame::styleForSelectionStart(Node *&nodeToRemove) const
1495 if (selectionController()->isNone())
1498 Position pos = selectionController()->selection().visibleStart().deepEquivalent();
1499 if (!pos.isCandidate())
1501 Node *node = pos.node();
1505 if (!d->m_typingStyle)
1506 return node->renderer()->style();
1508 ExceptionCode ec = 0;
1509 RefPtr<Element> styleElement = document()->createElementNS(xhtmlNamespaceURI, "span", ec);
1512 String styleText = d->m_typingStyle->cssText() + " display: inline";
1513 styleElement->setAttribute(styleAttr, styleText.impl(), ec);
1516 styleElement->appendChild(document()->createEditingTextNode(""), ec);
1519 node->parentNode()->appendChild(styleElement, ec);
1522 nodeToRemove = styleElement.get();
1523 return styleElement->renderer() ? styleElement->renderer()->style() : 0;
1526 void Frame::setSelectionFromNone()
1528 // Put a caret inside the body if the entire frame is editable (either the
1529 // entire WebView is editable or designMode is on for this document).
1530 Document *doc = document();
1531 if (!doc || !selectionController()->isNone() || !isContentEditable())
1534 Node* node = doc->documentElement();
1535 while (node && !node->hasTagName(bodyTag))
1536 node = node->traverseNextNode();
1538 selectionController()->setSelection(Selection(Position(node, 0), DOWNSTREAM));
1541 bool Frame::isActive() const
1543 return d->m_isActive;
1546 void Frame::setIsActive(bool flag)
1548 if (d->m_isActive == flag)
1550 d->m_isActive = flag;
1552 // Because RenderObject::selectionBackgroundColor() and
1553 // RenderObject::selectionForegroundColor() check if the frame is active,
1554 // we have to update places those colors were painted.
1556 d->m_view->updateContents(enclosingIntRect(selectionRect()));
1558 // Caret appears in the active frame.
1560 setSelectionFromNone();
1561 setCaretVisible(flag);
1563 // Because CSSStyleSelector::checkOneSelector() and
1564 // RenderTheme::isFocused() check if the frame is active, we have to
1565 // update style and theme state that depended on those.
1567 if (Node* node = d->m_doc->focusedNode()) {
1569 if (RenderObject* renderer = node->renderer())
1570 if (renderer && renderer->style()->hasAppearance())
1571 theme()->stateChanged(renderer, FocusState);
1575 // Secure keyboard entry is set by the active frame.
1576 if (d->m_doc->useSecureKeyboardEntryWhenActive())
1577 setUseSecureKeyboardEntry(flag);
1580 void Frame::setWindowHasFocus(bool flag)
1582 if (d->m_windowHasFocus == flag)
1584 d->m_windowHasFocus = flag;
1586 if (Document *doc = document())
1587 doc->dispatchWindowEvent(flag ? focusEvent : blurEvent, false, false);
1590 bool Frame::inViewSourceMode() const
1592 return d->m_inViewSourceMode;
1595 void Frame::setInViewSourceMode(bool mode) const
1597 d->m_inViewSourceMode = mode;
1600 UChar Frame::backslashAsCurrencySymbol() const
1602 Document *doc = document();
1605 TextResourceDecoder *decoder = doc->decoder();
1609 return decoder->encoding().backslashAsCurrencySymbol();
1612 static bool isInShadowTree(Node* node)
1614 for (Node* n = node; n; n = n->parentNode())
1615 if (n->isShadowNode())
1620 // Searches from the beginning of the document if nothing is selected.
1621 bool Frame::findString(const String& target, bool forward, bool caseFlag, bool wrapFlag, bool startInSelection)
1623 if (target.isEmpty() || !document())
1626 // Start from an edge of the selection, if there's a selection that's not in shadow content. Which edge
1627 // is used depends on whether we're searching forward or backward, and whether startInSelection is set.
1628 RefPtr<Range> searchRange(rangeOfContents(document()));
1629 Selection selection(selectionController()->selection());
1630 Node* selectionBaseNode = selection.base().node();
1632 // FIXME 3099526: We don't search in the shadow trees (e.g. text fields and textareas), though we'd like to
1633 // someday. If we don't explicitly skip them here, we'll miss hits in the regular content.
1634 bool selectionIsInMainContent = selectionBaseNode && !isInShadowTree(selectionBaseNode);
1636 if (selectionIsInMainContent) {
1638 setStart(searchRange.get(), startInSelection ? selection.visibleStart() : selection.visibleEnd());
1640 setEnd(searchRange.get(), startInSelection ? selection.visibleEnd() : selection.visibleStart());
1642 RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, forward, caseFlag));
1643 // If we started in the selection and the found range exactly matches the existing selection, find again.
1644 // Build a selection with the found range to remove collapsed whitespace.
1645 // Compare ranges instead of selection objects to ignore the way that the current selection was made.
1646 if (startInSelection && selectionIsInMainContent && *Selection(resultRange.get()).toRange() == *selection.toRange()) {
1647 searchRange = rangeOfContents(document());
1649 setStart(searchRange.get(), selection.visibleEnd());
1651 setEnd(searchRange.get(), selection.visibleStart());
1652 resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
1657 // If we didn't find anything and we're wrapping, search again in the entire document (this will
1658 // redundantly re-search the area already searched in some cases).
1659 if (resultRange->collapsed(exception) && wrapFlag) {
1660 searchRange = rangeOfContents(document());
1661 resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
1662 // We used to return false here if we ended up with the same range that we started with
1663 // (e.g., the selection was already the only instance of this text). But we decided that
1664 // this should be a success case instead, so we'll just fall through in that case.
1667 if (resultRange->collapsed(exception))
1670 selectionController()->setSelection(Selection(resultRange.get(), DOWNSTREAM));
1675 unsigned Frame::markAllMatchesForText(const String& target, bool caseFlag, unsigned limit)
1677 if (target.isEmpty() || !document())
1680 RefPtr<Range> searchRange(rangeOfContents(document()));
1683 unsigned matchCount = 0;
1685 RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, true, caseFlag));
1686 if (resultRange->collapsed(exception))
1689 // A non-collapsed result range can in some funky whitespace cases still not
1690 // advance the range's start position (4509328). Break to avoid infinite loop.
1691 VisiblePosition newStart = endVisiblePosition(resultRange.get(), DOWNSTREAM);
1692 if (newStart == startVisiblePosition(searchRange.get(), DOWNSTREAM))
1697 document()->addMarker(resultRange.get(), DocumentMarker::TextMatch);
1699 // Stop looking if we hit the specified limit. A limit of 0 means no limit.
1700 if (limit > 0 && matchCount >= limit)
1703 setStart(searchRange.get(), newStart);
1706 // Do a "fake" paint in order to execute the code that computes the rendered rect for
1708 Document* doc = document();
1709 if (doc && d->m_view && renderer()) {
1710 doc->updateLayout(); // Ensure layout is up to date.
1711 IntRect visibleRect(enclosingIntRect(d->m_view->visibleContentRect()));
1712 GraphicsContext context((PlatformGraphicsContext*)0);
1713 context.setPaintingDisabled(true);
1714 paint(&context, visibleRect);
1720 bool Frame::markedTextMatchesAreHighlighted() const
1722 return d->m_highlightTextMatches;
1725 void Frame::setMarkedTextMatchesAreHighlighted(bool flag)
1727 if (flag == d->m_highlightTextMatches || !document())
1730 d->m_highlightTextMatches = flag;
1731 document()->repaintMarkers(DocumentMarker::TextMatch);
1734 FrameTree* Frame::tree() const
1736 return &d->m_treeNode;
1739 DOMWindow* Frame::domWindow() const
1741 if (!d->m_domWindow)
1742 d->m_domWindow = new DOMWindow(const_cast<Frame*>(this));
1744 return d->m_domWindow.get();
1747 Page* Frame::page() const
1752 EventHandler* Frame::eventHandler() const
1754 return &d->m_eventHandler;
1757 void Frame::pageDestroyed()
1759 if (Frame* parent = tree()->parent())
1760 parent->loader()->checkLoadComplete();
1762 if (d->m_page && d->m_page->focusController()->focusedFrame() == this)
1763 d->m_page->focusController()->setFocusedFrame(0);
1765 // This will stop any JS timers
1766 if (d->m_jscript && d->m_jscript->haveInterpreter())
1767 if (KJS::Window* w = KJS::Window::retrieveWindow(this))
1768 w->disconnectFrame();
1773 void Frame::disconnectOwnerElement()
1775 if (d->m_ownerElement) {
1776 d->m_ownerElement->m_contentFrame = 0;
1778 d->m_page->decrementFrameCount();
1780 d->m_ownerElement = 0;
1783 String Frame::documentTypeString() const
1785 if (Document *doc = document())
1786 if (DocumentType *doctype = doc->realDocType())
1787 return doctype->toString();
1792 bool Frame::prohibitsScrolling() const
1794 return d->m_prohibitsScrolling;
1797 void Frame::setProhibitsScrolling(bool prohibit)
1799 d->m_prohibitsScrolling = prohibit;
1802 void Frame::focusWindow()
1807 // If we're a top level window, bring the window to the front.
1808 if (!tree()->parent())
1809 page()->chrome()->focus();
1811 eventHandler()->focusDocumentView();
1814 void Frame::unfocusWindow()
1819 // If we're a top level window, deactivate the window.
1820 if (!tree()->parent())
1821 page()->chrome()->unfocus();
1824 bool Frame::shouldClose()
1826 Chrome* chrome = page() ? page()->chrome() : 0;
1827 if (!chrome || !chrome->canRunBeforeUnloadConfirmPanel())
1830 RefPtr<Document> doc = document();
1833 HTMLElement* body = doc->body();
1837 RefPtr<BeforeUnloadEvent> beforeUnloadEvent = new BeforeUnloadEvent;
1838 beforeUnloadEvent->setTarget(doc);
1839 doc->handleWindowEvent(beforeUnloadEvent.get(), false);
1841 if (!beforeUnloadEvent->defaultPrevented() && doc)
1842 doc->defaultEventHandler(beforeUnloadEvent.get());
1843 if (beforeUnloadEvent->result().isNull())
1846 String text = beforeUnloadEvent->result();
1847 text.replace('\\', backslashAsCurrencySymbol());
1849 return chrome->runBeforeUnloadConfirmPanel(text, this);
1852 void Frame::scheduleClose()
1857 Chrome* chrome = page() ? page()->chrome() : 0;
1859 chrome->closeWindowSoon();
1862 void Frame::respondToChangedSelection(const Selection& oldSelection, bool closeTyping)
1865 bool isContinuousSpellCheckingEnabled = editor()->isContinuousSpellCheckingEnabled();
1866 bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled && editor()->isGrammarCheckingEnabled();
1867 if (isContinuousSpellCheckingEnabled) {
1868 Selection newAdjacentWords;
1869 Selection newSelectedSentence;
1870 if (selectionController()->selection().isContentEditable()) {
1871 VisiblePosition newStart(selectionController()->selection().visibleStart());
1872 newAdjacentWords = Selection(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
1873 if (isContinuousGrammarCheckingEnabled)
1874 newSelectedSentence = Selection(startOfSentence(newStart), endOfSentence(newStart));
1877 // When typing we check spelling elsewhere, so don't redo it here.
1878 // If this is a change in selection resulting from a delete operation,
1879 // oldSelection may no longer be in the document.
1880 if (closeTyping && oldSelection.isContentEditable() && oldSelection.start().node() && oldSelection.start().node()->inDocument()) {
1881 VisiblePosition oldStart(oldSelection.visibleStart());
1882 Selection oldAdjacentWords = Selection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
1883 if (oldAdjacentWords != newAdjacentWords) {
1884 editor()->markMisspellings(oldAdjacentWords);
1885 if (isContinuousGrammarCheckingEnabled) {
1886 Selection oldSelectedSentence = Selection(startOfSentence(oldStart), endOfSentence(oldStart));
1887 if (oldSelectedSentence != newSelectedSentence)
1888 editor()->markBadGrammar(oldSelectedSentence);
1893 // This only erases markers that are in the first unit (word or sentence) of the selection.
1894 // Perhaps peculiar, but it matches AppKit.
1895 if (RefPtr<Range> wordRange = newAdjacentWords.toRange())
1896 document()->removeMarkers(wordRange.get(), DocumentMarker::Spelling);
1897 if (RefPtr<Range> sentenceRange = newSelectedSentence.toRange())
1898 document()->removeMarkers(sentenceRange.get(), DocumentMarker::Grammar);
1901 // When continuous spell checking is off, existing markers disappear after the selection changes.
1902 if (!isContinuousSpellCheckingEnabled)
1903 document()->removeMarkers(DocumentMarker::Spelling);
1904 if (!isContinuousGrammarCheckingEnabled)
1905 document()->removeMarkers(DocumentMarker::Grammar);
1908 editor()->respondToChangedSelection(oldSelection);
1911 VisiblePosition Frame::visiblePositionForPoint(const IntPoint& framePoint)
1913 HitTestResult result = eventHandler()->hitTestResultAtPoint(framePoint, true);
1914 Node* node = result.innerNode();
1916 return VisiblePosition();
1917 RenderObject* renderer = node->renderer();
1919 return VisiblePosition();
1920 VisiblePosition visiblePos = renderer->positionForCoordinates(result.localPoint().x(), result.localPoint().y());
1921 if (visiblePos.isNull())
1922 visiblePos = VisiblePosition(Position(node, 0));
1926 Document* Frame::documentAtPoint(const IntPoint& point)
1931 IntPoint pt = view()->windowToContents(point);
1932 HitTestResult result = HitTestResult(pt);
1935 result = eventHandler()->hitTestResultAtPoint(pt, false);
1936 return result.innerNode() ? result.innerNode()->document() : 0;
1939 FramePrivate::FramePrivate(Page* page, Frame* parent, Frame* thisFrame, HTMLFrameOwnerElement* ownerElement,
1940 FrameLoaderClient* frameLoaderClient)
1942 , m_treeNode(thisFrame, parent)
1943 , m_ownerElement(ownerElement)
1945 , m_zoomFactor(parent ? parent->d->m_zoomFactor : 100)
1946 , m_selectionController(thisFrame)
1947 , m_caretBlinkTimer(thisFrame, &Frame::caretBlinkTimerFired)
1948 , m_editor(thisFrame)
1949 , m_eventHandler(thisFrame)
1950 , m_caretVisible(false)
1951 , m_caretPaint(true)
1953 , m_isPainting(false)
1954 , m_lifeSupportTimer(thisFrame, &Frame::lifeSupportTimerFired)
1955 , m_loader(new FrameLoader(thisFrame, frameLoaderClient))
1956 , m_userStyleSheetLoader(0)
1957 , m_paintRestriction(PaintRestrictionNone)
1958 , m_highlightTextMatches(false)
1959 , m_windowHasFocus(false)
1960 , m_inViewSourceMode(false)
1962 , m_prohibitsScrolling(false)
1963 , m_windowScriptNPObject(0)
1965 , m_windowScriptObject(nil)
1971 FramePrivate::~FramePrivate()
1977 } // namespace WebCore