Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(362)

Unified Diff: third_party/WebKit/WebCore/page/Frame.cpp

Issue 21184: WebKit merge 40722:40785 (part 1) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 11 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/WebCore/page/Frame.cpp
===================================================================
--- third_party/WebKit/WebCore/page/Frame.cpp (revision 9391)
+++ third_party/WebKit/WebCore/page/Frame.cpp (working copy)
@@ -1,1835 +1,1831 @@
-/*
- * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
- * 1999 Lars Knoll <knoll@kde.org>
- * 1999 Antti Koivisto <koivisto@kde.org>
- * 2000 Simon Hausmann <hausmann@kde.org>
- * 2000 Stefan Schimanski <1Stein@gmx.de>
- * 2001 George Staikos <staikos@kde.org>
- * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
- * Copyright (C) 2005 Alexey Proskuryakov <ap@nypop.com>
- * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
- * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#include "config.h"
-#include "Frame.h"
-
-#include "ApplyStyleCommand.h"
-#include "BeforeUnloadEvent.h"
-#include "CSSComputedStyleDeclaration.h"
-#include "CSSProperty.h"
-#include "CSSPropertyNames.h"
-#include "CachedCSSStyleSheet.h"
-#include "DOMWindow.h"
-#include "DocLoader.h"
-#include "DocumentType.h"
-#include "EditingText.h"
-#include "EditorClient.h"
-#include "EventNames.h"
-#include "FocusController.h"
-#include "FloatQuad.h"
-#include "FrameLoader.h"
-#include "FrameView.h"
-#include "GraphicsContext.h"
-#include "HTMLDocument.h"
-#include "HTMLFormElement.h"
-#include "HTMLFrameElementBase.h"
-#include "HTMLFormControlElement.h"
-#include "HTMLNames.h"
-#include "HTMLTableCellElement.h"
-#include "HitTestResult.h"
-#include "Logging.h"
-#include "markup.h"
-#include "MediaFeatureNames.h"
-#include "Navigator.h"
-#include "NodeList.h"
-#include "Page.h"
-#include "RegularExpression.h"
-#include "RenderPart.h"
-#include "RenderTableCell.h"
-#include "RenderTextControl.h"
-#include "RenderTheme.h"
-#include "RenderView.h"
-#include "ScriptController.h"
-#include "Settings.h"
-#include "TextIterator.h"
-#include "TextResourceDecoder.h"
-#include "XMLNames.h"
-#include "ScriptController.h"
-#include "npruntime_impl.h"
-#include "visible_units.h"
-#include <wtf/RefCountedLeakCounter.h>
-#include <wtf/StdLibExtras.h>
-
-#if USE(JSC)
-#include "JSDOMWindowShell.h"
-#include "runtime_root.h"
-#endif
-
-#if FRAME_LOADS_USER_STYLESHEET
-#include "UserStyleSheetLoader.h"
-#endif
-
-#if ENABLE(SVG)
-#include "SVGDocument.h"
-#include "SVGDocumentExtensions.h"
-#include "SVGNames.h"
-#include "XLinkNames.h"
-#endif
-
-#if ENABLE(WML)
-#include "WMLNames.h"
-#endif
-
-using namespace std;
-
-namespace WebCore {
-
-using namespace HTMLNames;
-
-#ifndef NDEBUG
-static WTF::RefCountedLeakCounter frameCounter("Frame");
-#endif
-
-static inline Frame* parentFromOwnerElement(HTMLFrameOwnerElement* ownerElement)
-{
- if (!ownerElement)
- return 0;
- return ownerElement->document()->frame();
-}
-
-Frame::Frame(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* frameLoaderClient)
- : m_page(page)
- , m_treeNode(this, parentFromOwnerElement(ownerElement))
- , m_loader(this, frameLoaderClient)
- , m_ownerElement(ownerElement)
- , m_script(this)
- , m_selectionGranularity(CharacterGranularity)
- , m_selectionController(this)
- , m_caretBlinkTimer(this, &Frame::caretBlinkTimerFired)
- , m_editor(this)
- , m_eventHandler(this)
- , m_animationController(this)
- , m_lifeSupportTimer(this, &Frame::lifeSupportTimerFired)
- , m_caretVisible(false)
- , m_caretPaint(true)
- , m_highlightTextMatches(false)
- , m_inViewSourceMode(false)
- , m_needsReapplyStyles(false)
- , m_isDisconnected(false)
- , m_excludeFromTextSearch(false)
-#if FRAME_LOADS_USER_STYLESHEET
- , m_userStyleSheetLoader(0)
-#endif
-{
- Frame* parent = parentFromOwnerElement(ownerElement);
- m_zoomFactor = parent ? parent->m_zoomFactor : 1.0f;
-
- AtomicString::init();
- HTMLNames::init();
- QualifiedName::init();
- MediaFeatureNames::init();
-
-#if ENABLE(SVG)
- SVGNames::init();
- XLinkNames::init();
-#endif
-
-#if ENABLE(WML)
- WMLNames::init();
-#endif
-
- XMLNames::init();
-
- if (!ownerElement)
- page->setMainFrame(this);
- else {
- page->incrementFrameCount();
- // Make sure we will not end up with two frames referencing the same owner element.
- ASSERT((!(ownerElement->m_contentFrame)) || (ownerElement->m_contentFrame->ownerElement() != ownerElement));
- ownerElement->m_contentFrame = this;
- }
-
-#ifndef NDEBUG
- frameCounter.increment();
-#endif
-}
-
-Frame::~Frame()
-{
- setView(0);
- loader()->clearRecordedFormValues();
- loader()->cancelAndClear();
-
- // FIXME: We should not be doing all this work inside the destructor
-
- ASSERT(!m_lifeSupportTimer.isActive());
-
-#ifndef NDEBUG
- frameCounter.decrement();
-#endif
-
-#if USE(JSC)
- if (m_script.haveWindowShell())
- m_script.windowShell()->disconnectFrame();
-#elif USE(V8)
- m_script.disconnectFrame();
-#endif
-
- disconnectOwnerElement();
-
- if (m_domWindow)
- m_domWindow->disconnectFrame();
-
- HashSet<DOMWindow*>::iterator end = m_liveFormerWindows.end();
- for (HashSet<DOMWindow*>::iterator it = m_liveFormerWindows.begin(); it != end; ++it)
- (*it)->disconnectFrame();
-
- if (m_view) {
- m_view->hide();
- m_view->clearFrame();
- }
-
- ASSERT(!m_lifeSupportTimer.isActive());
-
-#if FRAME_LOADS_USER_STYLESHEET
- delete m_userStyleSheetLoader;
-#endif
-}
-
-void Frame::init()
-{
- m_loader.init();
-}
-
-FrameLoader* Frame::loader() const
-{
- return &m_loader;
-}
-
-FrameView* Frame::view() const
-{
- return m_view.get();
-}
-
-void Frame::setView(FrameView* view)
-{
- // Detach the document now, so any onUnload handlers get run - if
- // we wait until the view is destroyed, then things won't be
- // hooked up enough for some JavaScript calls to work.
- if (!view && m_doc && m_doc->attached() && !m_doc->inPageCache()) {
- // FIXME: We don't call willRemove here. Why is that OK?
- m_doc->detach();
- if (m_view)
- m_view->unscheduleRelayout();
- }
- eventHandler()->clear();
-
- m_view = view;
-
- // Only one form submission is allowed per view of a part.
- // Since this part may be getting reused as a result of being
- // pulled from the back/forward cache, reset this flag.
- loader()->resetMultipleFormSubmissionProtection();
-}
-
-ScriptController* Frame::script()
-{
- return &m_script;
-}
-
-Document* Frame::document() const
-{
- return m_doc.get();
-}
-
-void Frame::setDocument(PassRefPtr<Document> newDoc)
-{
- if (m_doc && m_doc->attached() && !m_doc->inPageCache()) {
- // FIXME: We don't call willRemove here. Why is that OK?
- m_doc->detach();
- }
-
- m_doc = newDoc;
- if (m_doc && selection()->isFocusedAndActive())
- setUseSecureKeyboardEntry(m_doc->useSecureKeyboardEntryWhenActive());
-
- if (m_doc && !m_doc->attached())
- m_doc->attach();
-
- // Update the cached 'document' property, which is now stale.
- m_script.updateDocument();
-}
-
-Settings* Frame::settings() const
-{
- return m_page ? m_page->settings() : 0;
-}
-
-String Frame::selectedText() const
-{
- return plainText(selection()->toRange().get());
-}
-
-IntRect Frame::firstRectForRange(Range* range) const
-{
- int extraWidthToEndOfLine = 0;
- ExceptionCode ec = 0;
- ASSERT(range->startContainer(ec));
- ASSERT(range->endContainer(ec));
-
- InlineBox* startInlineBox;
- int startCaretOffset;
- range->startPosition().getInlineBoxAndOffset(DOWNSTREAM, startInlineBox, startCaretOffset);
-
- RenderObject* startRenderer = range->startContainer(ec)->renderer();
- IntRect startCaretRect = startRenderer->localCaretRect(startInlineBox, startCaretOffset, &extraWidthToEndOfLine);
- if (startCaretRect != IntRect())
- startCaretRect = startRenderer->localToAbsoluteQuad(FloatRect(startCaretRect)).enclosingBoundingBox();
-
- InlineBox* endInlineBox;
- int endCaretOffset;
- range->endPosition().getInlineBoxAndOffset(UPSTREAM, endInlineBox, endCaretOffset);
-
- RenderObject* endRenderer = range->endContainer(ec)->renderer();
- IntRect endCaretRect = endRenderer->localCaretRect(endInlineBox, endCaretOffset);
- if (endCaretRect != IntRect())
- endCaretRect = endRenderer->localToAbsoluteQuad(FloatRect(endCaretRect)).enclosingBoundingBox();
-
- if (startCaretRect.y() == endCaretRect.y()) {
- // start and end are on the same line
- return IntRect(min(startCaretRect.x(), endCaretRect.x()),
- startCaretRect.y(),
- abs(endCaretRect.x() - startCaretRect.x()),
- max(startCaretRect.height(), endCaretRect.height()));
- }
-
- // start and end aren't on the same line, so go from start to the end of its line
- return IntRect(startCaretRect.x(),
- startCaretRect.y(),
- startCaretRect.width() + extraWidthToEndOfLine,
- startCaretRect.height());
-}
-
-SelectionController* Frame::selection() const
-{
- return &m_selectionController;
-}
-
-Editor* Frame::editor() const
-{
- return &m_editor;
-}
-
-TextGranularity Frame::selectionGranularity() const
-{
- return m_selectionGranularity;
-}
-
-void Frame::setSelectionGranularity(TextGranularity granularity)
-{
- m_selectionGranularity = granularity;
-}
-
-SelectionController* Frame::dragCaretController() const
-{
- return m_page->dragCaretController();
-}
-
-
-AnimationController* Frame::animation() const
-{
- return &m_animationController;
-}
-
-static RegularExpression* createRegExpForLabels(const Vector<String>& labels)
-{
- // REVIEW- version of this call in FrameMac.mm caches based on the NSArray ptrs being
- // the same across calls. We can't do that.
-
- DEFINE_STATIC_LOCAL(RegularExpression, wordRegExp, ("\\w", TextCaseSensitive));
- String pattern("(");
- unsigned int numLabels = labels.size();
- unsigned int i;
- for (i = 0; i < numLabels; i++) {
- String label = labels[i];
-
- bool startsWithWordChar = false;
- bool endsWithWordChar = false;
- if (label.length() != 0) {
- startsWithWordChar = wordRegExp.match(label.substring(0, 1)) >= 0;
- endsWithWordChar = wordRegExp.match(label.substring(label.length() - 1, 1)) >= 0;
- }
-
- if (i != 0)
- pattern.append("|");
- // Search for word boundaries only if label starts/ends with "word characters".
- // If we always searched for word boundaries, this wouldn't work for languages
- // such as Japanese.
- if (startsWithWordChar) {
- pattern.append("\\b");
- }
- pattern.append(label);
- if (endsWithWordChar) {
- pattern.append("\\b");
- }
- }
- pattern.append(")");
- return new RegularExpression(pattern, TextCaseInsensitive);
-}
-
-String Frame::searchForLabelsAboveCell(RegularExpression* regExp, HTMLTableCellElement* cell)
-{
- RenderTableCell* cellRenderer = static_cast<RenderTableCell*>(cell->renderer());
-
- if (cellRenderer && cellRenderer->isTableCell()) {
- RenderTableCell* cellAboveRenderer = cellRenderer->table()->cellAbove(cellRenderer);
-
- if (cellAboveRenderer) {
- HTMLTableCellElement* aboveCell =
- static_cast<HTMLTableCellElement*>(cellAboveRenderer->element());
-
- if (aboveCell) {
- // search within the above cell we found for a match
- for (Node* n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) {
- if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
- // For each text chunk, run the regexp
- String nodeString = n->nodeValue();
- int pos = regExp->searchRev(nodeString);
- if (pos >= 0)
- return nodeString.substring(pos, regExp->matchedLength());
- }
- }
- }
- }
- }
- // Any reason in practice to search all cells in that are above cell?
- return String();
-}
-
-String Frame::searchForLabelsBeforeElement(const Vector<String>& labels, Element* element)
-{
- OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels));
- // We stop searching after we've seen this many chars
- const unsigned int charsSearchedThreshold = 500;
- // This is the absolute max we search. We allow a little more slop than
- // charsSearchedThreshold, to make it more likely that we'll search whole nodes.
- const unsigned int maxCharsSearched = 600;
- // If the starting element is within a table, the cell that contains it
- HTMLTableCellElement* startingTableCell = 0;
- bool searchedCellAbove = false;
-
- // walk backwards in the node tree, until another element, or form, or end of tree
- int unsigned lengthSearched = 0;
- Node* n;
- for (n = element->traversePreviousNode();
- n && lengthSearched < charsSearchedThreshold;
- n = n->traversePreviousNode())
- {
- if (n->hasTagName(formTag)
- || (n->isHTMLElement() && static_cast<Element*>(n)->isFormControlElement()))
- {
- // We hit another form element or the start of the form - bail out
- break;
- } else if (n->hasTagName(tdTag) && !startingTableCell) {
- startingTableCell = static_cast<HTMLTableCellElement*>(n);
- } else if (n->hasTagName(trTag) && startingTableCell) {
- String result = searchForLabelsAboveCell(regExp.get(), startingTableCell);
- if (!result.isEmpty())
- return result;
- searchedCellAbove = true;
- } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
- // For each text chunk, run the regexp
- String nodeString = n->nodeValue();
- // add 100 for slop, to make it more likely that we'll search whole nodes
- if (lengthSearched + nodeString.length() > maxCharsSearched)
- nodeString = nodeString.right(charsSearchedThreshold - lengthSearched);
- int pos = regExp->searchRev(nodeString);
- if (pos >= 0)
- return nodeString.substring(pos, regExp->matchedLength());
- lengthSearched += nodeString.length();
- }
- }
-
- // If we started in a cell, but bailed because we found the start of the form or the
- // previous element, we still might need to search the row above us for a label.
- if (startingTableCell && !searchedCellAbove) {
- return searchForLabelsAboveCell(regExp.get(), startingTableCell);
- }
- return String();
-}
-
-String Frame::matchLabelsAgainstElement(const Vector<String>& labels, Element* element)
-{
- String name = element->getAttribute(nameAttr);
- if (name.isEmpty())
- return String();
-
- // Make numbers and _'s in field names behave like word boundaries, e.g., "address2"
- replace(name, RegularExpression("\\d", TextCaseSensitive), " ");
- name.replace('_', ' ');
-
- OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels));
- // Use the largest match we can find in the whole name string
- int pos;
- int length;
- int bestPos = -1;
- int bestLength = -1;
- int start = 0;
- do {
- pos = regExp->match(name, start);
- if (pos != -1) {
- length = regExp->matchedLength();
- if (length >= bestLength) {
- bestPos = pos;
- bestLength = length;
- }
- start = pos + 1;
- }
- } while (pos != -1);
-
- if (bestPos != -1)
- return name.substring(bestPos, bestLength);
- return String();
-}
-
-const Selection& Frame::mark() const
-{
- return m_mark;
-}
-
-void Frame::setMark(const Selection& s)
-{
- ASSERT(!s.base().node() || s.base().node()->document() == document());
- ASSERT(!s.extent().node() || s.extent().node()->document() == document());
- ASSERT(!s.start().node() || s.start().node()->document() == document());
- ASSERT(!s.end().node() || s.end().node()->document() == document());
-
- m_mark = s;
-}
-
-void Frame::notifyRendererOfSelectionChange(bool userTriggered)
-{
- RenderObject* renderer = 0;
- if (selection()->rootEditableElement())
- renderer = selection()->rootEditableElement()->shadowAncestorNode()->renderer();
-
- // If the current selection is in a textfield or textarea, notify the renderer that the selection has changed
- if (renderer && (renderer->isTextArea() || renderer->isTextField()))
- static_cast<RenderTextControl*>(renderer)->selectionChanged(userTriggered);
-}
-
-void Frame::invalidateSelection()
-{
- selection()->setNeedsLayout();
- selectionLayoutChanged();
-}
-
-void Frame::setCaretVisible(bool flag)
-{
- if (m_caretVisible == flag)
- return;
- clearCaretRectIfNeeded();
- m_caretVisible = flag;
- selectionLayoutChanged();
-}
-
-void Frame::clearCaretRectIfNeeded()
-{
-#if ENABLE(TEXT_CARET)
- if (m_caretPaint) {
- m_caretPaint = false;
- selection()->invalidateCaretRect();
- }
-#endif
-}
-
-// Helper function that tells whether a particular node is an element that has an entire
-// Frame and FrameView, a <frame>, <iframe>, or <object>.
-static bool isFrameElement(const Node *n)
-{
- if (!n)
- return false;
- RenderObject *renderer = n->renderer();
- if (!renderer || !renderer->isWidget())
- return false;
- Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
- return widget && widget->isFrameView();
-}
-
-void Frame::setFocusedNodeIfNeeded()
-{
- if (!document() || selection()->isNone() || !selection()->isFocusedAndActive())
- return;
-
- Node* target = selection()->rootEditableElement();
- if (target) {
- RenderObject* renderer = target->renderer();
-
- // Walk up the render tree to search for a node to focus.
- // Walking up the DOM tree wouldn't work for shadow trees, like those behind the engine-based text fields.
- while (renderer) {
- // We don't want to set focus on a subframe when selecting in a parent frame,
- // so add the !isFrameElement check here. There's probably a better way to make this
- // work in the long term, but this is the safest fix at this time.
- if (target && target->isMouseFocusable() && !isFrameElement(target)) {
- page()->focusController()->setFocusedNode(target, this);
- return;
- }
- renderer = renderer->parent();
- if (renderer)
- target = renderer->element();
- }
- document()->setFocusedNode(0);
- }
-}
-
-void Frame::selectionLayoutChanged()
-{
- bool caretRectChanged = selection()->recomputeCaretRect();
-
-#if ENABLE(TEXT_CARET)
- bool shouldBlink = m_caretVisible
- && selection()->isCaret() && selection()->isContentEditable();
-
- // If the caret moved, stop the blink timer so we can restart with a
- // black caret in the new location.
- if (caretRectChanged || !shouldBlink)
- m_caretBlinkTimer.stop();
-
- // Start blinking with a black caret. Be sure not to restart if we're
- // already blinking in the right location.
- if (shouldBlink && !m_caretBlinkTimer.isActive()) {
- if (double blinkInterval = theme()->caretBlinkInterval())
- m_caretBlinkTimer.startRepeating(blinkInterval);
-
- if (!m_caretPaint) {
- m_caretPaint = true;
- selection()->invalidateCaretRect();
- }
- }
-#else
- if (!caretRectChanged)
- return;
-#endif
-
- RenderView* view = contentRenderer();
- if (!view)
- return;
-
- Selection selection = this->selection()->selection();
-
- if (!selection.isRange())
- view->clearSelection();
- else {
- // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection.
- // Example: foo <a>bar</a>. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3]
- // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected
- // and will fill the gap before 'bar'.
- Position startPos = selection.start();
- if (startPos.downstream().isCandidate())
- startPos = startPos.downstream();
- Position endPos = selection.end();
- if (endPos.upstream().isCandidate())
- endPos = endPos.upstream();
-
- // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted
- // because we don't yet notify the SelectionController of text removal.
- if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
- RenderObject *startRenderer = startPos.node()->renderer();
- RenderObject *endRenderer = endPos.node()->renderer();
- view->setSelection(startRenderer, startPos.offset(), endRenderer, endPos.offset());
- }
- }
-}
-
-void Frame::caretBlinkTimerFired(Timer<Frame>*)
-{
-#if ENABLE(TEXT_CARET)
- ASSERT(m_caretVisible);
- ASSERT(selection()->isCaret());
- bool caretPaint = m_caretPaint;
- if (selection()->isCaretBlinkingSuspended() && caretPaint)
- return;
- m_caretPaint = !caretPaint;
- selection()->invalidateCaretRect();
-#endif
-}
-
-void Frame::paintCaret(GraphicsContext* p, int tx, int ty, const IntRect& clipRect) const
-{
-#if ENABLE(TEXT_CARET)
- if (m_caretPaint && m_caretVisible)
- selection()->paintCaret(p, tx, ty, clipRect);
-#endif
-}
-
-void Frame::paintDragCaret(GraphicsContext* p, int tx, int ty, const IntRect& clipRect) const
-{
-#if ENABLE(TEXT_CARET)
- SelectionController* dragCaretController = m_page->dragCaretController();
- ASSERT(dragCaretController->selection().isCaret());
- if (dragCaretController->selection().start().node()->document()->frame() == this)
- dragCaretController->paintCaret(p, tx, ty, clipRect);
-#endif
-}
-
-float Frame::zoomFactor() const
-{
- return m_zoomFactor;
-}
-
-bool Frame::isZoomFactorTextOnly() const
-{
- return m_page->settings()->zoomsTextOnly();
-}
-
-bool Frame::shouldApplyTextZoom() const
-{
- if (m_zoomFactor == 1.0f || !isZoomFactorTextOnly())
- return false;
-#if ENABLE(SVG)
- if (m_doc && m_doc->isSVGDocument())
- return false;
-#endif
- return true;
-}
-
-bool Frame::shouldApplyPageZoom() const
-{
- if (m_zoomFactor == 1.0f || isZoomFactorTextOnly())
- return false;
-#if ENABLE(SVG)
- if (m_doc && m_doc->isSVGDocument())
- return false;
-#endif
- return true;
-}
-
-void Frame::setZoomFactor(float percent, bool isTextOnly)
-{
- if (m_zoomFactor == percent && isZoomFactorTextOnly() == isTextOnly)
- return;
-
-#if ENABLE(SVG)
- // SVG doesn't care if the zoom factor is text only. It will always apply a
- // zoom to the whole SVG.
- if (m_doc && m_doc->isSVGDocument()) {
- if (!static_cast<SVGDocument*>(m_doc.get())->zoomAndPanEnabled())
- return;
- m_zoomFactor = percent;
- m_page->settings()->setZoomsTextOnly(true); // We do this to avoid doing any scaling of CSS pixels, since the SVG has its own notion of zoom.
- if (m_doc->renderer())
- m_doc->renderer()->repaint();
- return;
- }
-#endif
-
- m_zoomFactor = percent;
- m_page->settings()->setZoomsTextOnly(isTextOnly);
-
- if (m_doc)
- m_doc->recalcStyle(Node::Force);
-
- for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling())
- child->setZoomFactor(m_zoomFactor, isTextOnly);
-
- if (m_doc && m_doc->renderer() && m_doc->renderer()->needsLayout() && view()->didFirstLayout())
- view()->layout();
-}
-
-void Frame::setPrinting(bool printing, float minPageWidth, float maxPageWidth, bool adjustViewSize)
-{
- if (!m_doc)
- return;
-
- m_doc->setPrinting(printing);
- view()->setMediaType(printing ? "print" : "screen");
- m_doc->updateStyleSelector();
- view()->forceLayoutWithPageWidthRange(minPageWidth, maxPageWidth, adjustViewSize);
-
- for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling())
- child->setPrinting(printing, minPageWidth, maxPageWidth, adjustViewSize);
-}
-
-void Frame::setJSStatusBarText(const String& text)
-{
- m_kjsStatusBarText = text;
- if (m_page)
- m_page->chrome()->setStatusbarText(this, m_kjsStatusBarText);
-}
-
-void Frame::setJSDefaultStatusBarText(const String& text)
-{
- m_kjsDefaultStatusBarText = text;
- if (m_page)
- m_page->chrome()->setStatusbarText(this, m_kjsDefaultStatusBarText);
-}
-
-String Frame::jsStatusBarText() const
-{
- return m_kjsStatusBarText;
-}
-
-String Frame::jsDefaultStatusBarText() const
-{
- return m_kjsDefaultStatusBarText;
-}
-
-void Frame::setNeedsReapplyStyles()
-{
- if (m_needsReapplyStyles)
- return;
-
- m_needsReapplyStyles = true;
-
- // FrameView's "layout" timer includes reapplyStyles, so despite its
- // name, it's what we want to call here.
- if (view())
- view()->scheduleRelayout();
-}
-
-bool Frame::needsReapplyStyles() const
-{
- return m_needsReapplyStyles;
-}
-
-void Frame::reapplyStyles()
-{
- m_needsReapplyStyles = false;
-
- // FIXME: This call doesn't really make sense in a method called
- // "reapplyStyles". We should probably eventually move it into its own
- // method.
- if (m_doc)
- m_doc->docLoader()->setAutoLoadImages(m_page && m_page->settings()->loadsImagesAutomatically());
-
-#if FRAME_LOADS_USER_STYLESHEET
- const KURL userStyleSheetLocation = m_page ? m_page->settings()->userStyleSheetLocation() : KURL();
- if (!userStyleSheetLocation.isEmpty())
- setUserStyleSheetLocation(userStyleSheetLocation);
- else
- setUserStyleSheet(String());
-#endif
-
- // FIXME: It's not entirely clear why the following is needed.
- // The document automatically does this as required when you set the style sheet.
- // But we had problems when this code was removed. Details are in
- // <http://bugs.webkit.org/show_bug.cgi?id=8079>.
- if (m_doc)
- m_doc->updateStyleSelector();
-}
-
-bool Frame::shouldChangeSelection(const Selection& newSelection) const
-{
- return shouldChangeSelection(selection()->selection(), newSelection, newSelection.affinity(), false);
-}
-
-bool Frame::shouldChangeSelection(const Selection& oldSelection, const Selection& newSelection, EAffinity affinity, bool stillSelecting) const
-{
- return editor()->client()->shouldChangeSelectedRange(oldSelection.toRange().get(), newSelection.toRange().get(),
- affinity, stillSelecting);
-}
-
-bool Frame::shouldDeleteSelection(const Selection& selection) const
-{
- return editor()->client()->shouldDeleteRange(selection.toRange().get());
-}
-
-bool Frame::isContentEditable() const
-{
- if (m_editor.clientIsEditable())
- return true;
- if (!m_doc)
- return false;
- return m_doc->inDesignMode();
-}
-
-#if !PLATFORM(MAC)
-
-void Frame::setUseSecureKeyboardEntry(bool)
-{
-}
-
-#endif
-
-void Frame::updateSecureKeyboardEntryIfActive()
-{
- if (selection()->isFocusedAndActive())
- setUseSecureKeyboardEntry(m_doc->useSecureKeyboardEntryWhenActive());
-}
-
-CSSMutableStyleDeclaration *Frame::typingStyle() const
-{
- return m_typingStyle.get();
-}
-
-void Frame::setTypingStyle(CSSMutableStyleDeclaration *style)
-{
- m_typingStyle = style;
-}
-
-void Frame::clearTypingStyle()
-{
- m_typingStyle = 0;
-}
-
-void Frame::computeAndSetTypingStyle(CSSStyleDeclaration *style, EditAction editingAction)
-{
- if (!style || style->length() == 0) {
- clearTypingStyle();
- return;
- }
-
- // Calculate the current typing style.
- RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable();
- if (typingStyle()) {
- typingStyle()->merge(mutableStyle.get());
- mutableStyle = typingStyle();
- }
-
- RefPtr<CSSValue> unicodeBidi;
- RefPtr<CSSValue> direction;
- if (editingAction == EditActionSetWritingDirection) {
- unicodeBidi = mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
- direction = mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
- }
-
- Node* node = selection()->selection().visibleStart().deepEquivalent().node();
- computedStyle(node)->diff(mutableStyle.get());
-
- if (editingAction == EditActionSetWritingDirection && unicodeBidi) {
- ASSERT(unicodeBidi->isPrimitiveValue());
- mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent());
- if (direction) {
- ASSERT(direction->isPrimitiveValue());
- mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent());
- }
- }
-
- // Handle block styles, substracting these from the typing style.
- RefPtr<CSSMutableStyleDeclaration> blockStyle = mutableStyle->copyBlockProperties();
- blockStyle->diff(mutableStyle.get());
- if (document() && blockStyle->length() > 0)
- applyCommand(ApplyStyleCommand::create(document(), blockStyle.get(), editingAction));
-
- // Set the remaining style as the typing style.
- m_typingStyle = mutableStyle.release();
-}
-
-String Frame::selectionStartStylePropertyValue(int stylePropertyID) const
-{
- Node *nodeToRemove;
- RefPtr<CSSStyleDeclaration> selectionStyle = selectionComputedStyle(nodeToRemove);
- if (!selectionStyle)
- return String();
-
- String value = selectionStyle->getPropertyValue(stylePropertyID);
-
- if (nodeToRemove) {
- ExceptionCode ec = 0;
- nodeToRemove->remove(ec);
- ASSERT(ec == 0);
- }
-
- return value;
-}
-
-PassRefPtr<CSSComputedStyleDeclaration> Frame::selectionComputedStyle(Node*& nodeToRemove) const
-{
- nodeToRemove = 0;
-
- if (!document())
- return 0;
-
- if (selection()->isNone())
- return 0;
-
- RefPtr<Range> range(selection()->toRange());
- Position pos = range->editingStartPosition();
-
- Element *elem = pos.element();
- if (!elem)
- return 0;
-
- RefPtr<Element> styleElement = elem;
- ExceptionCode ec = 0;
-
- if (m_typingStyle) {
- styleElement = document()->createElementNS(xhtmlNamespaceURI, "span", ec);
- ASSERT(ec == 0);
-
- styleElement->setAttribute(styleAttr, m_typingStyle->cssText().impl(), ec);
- ASSERT(ec == 0);
-
- styleElement->appendChild(document()->createEditingTextNode(""), ec);
- ASSERT(ec == 0);
-
- if (elem->renderer() && elem->renderer()->canHaveChildren()) {
- elem->appendChild(styleElement, ec);
- } else {
- Node *parent = elem->parent();
- Node *next = elem->nextSibling();
-
- if (next) {
- parent->insertBefore(styleElement, next, ec);
- } else {
- parent->appendChild(styleElement, ec);
- }
- }
- ASSERT(ec == 0);
-
- nodeToRemove = styleElement.get();
- }
-
- return computedStyle(styleElement.release());
-}
-
-void Frame::textFieldDidBeginEditing(Element* e)
-{
- if (editor()->client())
- editor()->client()->textFieldDidBeginEditing(e);
-}
-
-void Frame::textFieldDidEndEditing(Element* e)
-{
- if (editor()->client())
- editor()->client()->textFieldDidEndEditing(e);
-}
-
-void Frame::textDidChangeInTextField(Element* e)
-{
- if (editor()->client())
- editor()->client()->textDidChangeInTextField(e);
-}
-
-bool Frame::doTextFieldCommandFromEvent(Element* e, KeyboardEvent* ke)
-{
- if (editor()->client())
- return editor()->client()->doTextFieldCommandFromEvent(e, ke);
-
- return false;
-}
-
-void Frame::textWillBeDeletedInTextField(Element* input)
-{
- if (editor()->client())
- editor()->client()->textWillBeDeletedInTextField(input);
-}
-
-void Frame::textDidChangeInTextArea(Element* e)
-{
- if (editor()->client())
- editor()->client()->textDidChangeInTextArea(e);
-}
-
-void Frame::applyEditingStyleToBodyElement() const
-{
- if (!m_doc)
- return;
-
- RefPtr<NodeList> list = m_doc->getElementsByTagName("body");
- unsigned len = list->length();
- for (unsigned i = 0; i < len; i++) {
- applyEditingStyleToElement(static_cast<Element*>(list->item(i)));
- }
-}
-
-void Frame::removeEditingStyleFromBodyElement() const
-{
- if (!m_doc)
- return;
-
- RefPtr<NodeList> list = m_doc->getElementsByTagName("body");
- unsigned len = list->length();
- for (unsigned i = 0; i < len; i++) {
- removeEditingStyleFromElement(static_cast<Element*>(list->item(i)));
- }
-}
-
-void Frame::applyEditingStyleToElement(Element* element) const
-{
- if (!element)
- return;
-
- CSSStyleDeclaration* style = element->style();
- ASSERT(style);
-
- ExceptionCode ec = 0;
- style->setProperty(CSSPropertyWordWrap, "break-word", false, ec);
- ASSERT(ec == 0);
- style->setProperty(CSSPropertyWebkitNbspMode, "space", false, ec);
- ASSERT(ec == 0);
- style->setProperty(CSSPropertyWebkitLineBreak, "after-white-space", false, ec);
- ASSERT(ec == 0);
-}
-
-void Frame::removeEditingStyleFromElement(Element*) const
-{
-}
-
-#ifndef NDEBUG
-static HashSet<Frame*>& keepAliveSet()
-{
- DEFINE_STATIC_LOCAL(HashSet<Frame*>, staticKeepAliveSet, ());
- return staticKeepAliveSet;
-}
-#endif
-
-void Frame::keepAlive()
-{
- if (m_lifeSupportTimer.isActive())
- return;
-#ifndef NDEBUG
- keepAliveSet().add(this);
-#endif
- ref();
- m_lifeSupportTimer.startOneShot(0);
-}
-
-#ifndef NDEBUG
-void Frame::cancelAllKeepAlive()
-{
- HashSet<Frame*>::iterator end = keepAliveSet().end();
- for (HashSet<Frame*>::iterator it = keepAliveSet().begin(); it != end; ++it) {
- Frame* frame = *it;
- frame->m_lifeSupportTimer.stop();
- frame->deref();
- }
- keepAliveSet().clear();
-}
-#endif
-
-void Frame::lifeSupportTimerFired(Timer<Frame>*)
-{
-#ifndef NDEBUG
- keepAliveSet().remove(this);
-#endif
- deref();
-}
-
-void Frame::clearDOMWindow()
-{
- if (m_domWindow) {
- m_liveFormerWindows.add(m_domWindow.get());
- m_domWindow->clear();
- }
- m_domWindow = 0;
-#if USE(V8)
- m_script.clearPluginObjects();
-#endif
-}
-
-RenderView* Frame::contentRenderer() const
-{
- Document* doc = document();
- if (!doc)
- return 0;
- RenderObject* object = doc->renderer();
- if (!object)
- return 0;
- ASSERT(object->isRenderView());
- return toRenderView(object);
-}
-
-HTMLFrameOwnerElement* Frame::ownerElement() const
-{
- return m_ownerElement;
-}
-
-RenderPart* Frame::ownerRenderer() const
-{
- HTMLFrameOwnerElement* ownerElement = m_ownerElement;
- if (!ownerElement)
- return 0;
- RenderObject* object = ownerElement->renderer();
- if (!object)
- return 0;
- // FIXME: If <object> is ever fixed to disassociate itself from frames
- // that it has started but canceled, then this can turn into an ASSERT
- // since m_ownerElement would be 0 when the load is canceled.
- // https://bugs.webkit.org/show_bug.cgi?id=18585
- if (!object->isRenderPart())
- return 0;
- return static_cast<RenderPart*>(object);
-}
-
-bool Frame::isDisconnected() const
-{
- return m_isDisconnected;
-}
-
-void Frame::setIsDisconnected(bool isDisconnected)
-{
- m_isDisconnected = isDisconnected;
-}
-
-bool Frame::excludeFromTextSearch() const
-{
- return m_excludeFromTextSearch;
-}
-
-void Frame::setExcludeFromTextSearch(bool exclude)
-{
- m_excludeFromTextSearch = exclude;
-}
-
-// returns FloatRect because going through IntRect would truncate any floats
-FloatRect Frame::selectionBounds(bool clipToVisibleContent) const
-{
- RenderView* root = contentRenderer();
- FrameView* view = m_view.get();
- if (!root || !view)
- return IntRect();
-
- IntRect selectionRect = root->selectionBounds(clipToVisibleContent);
- return clipToVisibleContent ? intersection(selectionRect, view->visibleContentRect()) : selectionRect;
-}
-
-void Frame::selectionTextRects(Vector<FloatRect>& rects, bool clipToVisibleContent) const
-{
- RenderView* root = contentRenderer();
- if (!root)
- return;
-
- RefPtr<Range> selectedRange = selection()->toRange();
-
- Vector<IntRect> intRects;
- selectedRange->addLineBoxRects(intRects, true);
-
- unsigned size = intRects.size();
- FloatRect visibleContentRect = m_view->visibleContentRect();
- for (unsigned i = 0; i < size; ++i)
- if (clipToVisibleContent)
- rects.append(intersection(intRects[i], visibleContentRect));
- else
- rects.append(intRects[i]);
-}
-
-
-// Scans logically forward from "start", including any child frames
-static HTMLFormElement *scanForForm(Node *start)
-{
- Node *n;
- for (n = start; n; n = n->traverseNextNode()) {
- if (n->hasTagName(formTag))
- return static_cast<HTMLFormElement*>(n);
- else if (n->isHTMLElement() && static_cast<Element*>(n)->isFormControlElement())
- return static_cast<HTMLFormControlElement*>(n)->form();
- else if (n->hasTagName(frameTag) || n->hasTagName(iframeTag)) {
- Node *childDoc = static_cast<HTMLFrameElementBase*>(n)->contentDocument();
- if (HTMLFormElement *frameResult = scanForForm(childDoc))
- return frameResult;
- }
- }
- return 0;
-}
-
-// We look for either the form containing the current focus, or for one immediately after it
-HTMLFormElement *Frame::currentForm() const
-{
- // start looking either at the active (first responder) node, or where the selection is
- Node *start = m_doc ? m_doc->focusedNode() : 0;
- if (!start)
- start = selection()->start().node();
-
- // try walking up the node tree to find a form element
- Node *n;
- for (n = start; n; n = n->parentNode()) {
- if (n->hasTagName(formTag))
- return static_cast<HTMLFormElement*>(n);
- else if (n->isHTMLElement() && static_cast<Element*>(n)->isFormControlElement())
- return static_cast<HTMLFormControlElement*>(n)->form();
- }
-
- // try walking forward in the node tree to find a form element
- return start ? scanForForm(start) : 0;
-}
-
-// FIXME: should this go in SelectionController?
-void Frame::revealSelection(const RenderLayer::ScrollAlignment& alignment) const
-{
- IntRect rect;
-
- switch (selection()->state()) {
- case Selection::NONE:
- return;
-
- case Selection::CARET:
- rect = selection()->absoluteCaretBounds();
- break;
-
- case Selection::RANGE:
- rect = enclosingIntRect(selectionBounds(false));
- break;
- }
-
- Position start = selection()->start();
-
- ASSERT(start.node());
- if (start.node() && start.node()->renderer()) {
- // FIXME: This code only handles scrolling the startContainer's layer, but
- // the selection rect could intersect more than just that.
- // See <rdar://problem/4799899>.
- if (RenderLayer *layer = start.node()->renderer()->enclosingLayer())
- layer->scrollRectToVisible(rect, false, alignment, alignment);
- }
-}
-
-void Frame::revealCaret(const RenderLayer::ScrollAlignment& alignment) const
-{
- if (selection()->isNone())
- return;
-
- Position extent = selection()->extent();
- if (extent.node() && extent.node()->renderer()) {
- IntRect extentRect = VisiblePosition(extent).absoluteCaretBounds();
- RenderLayer* layer = extent.node()->renderer()->enclosingLayer();
- if (layer)
- layer->scrollRectToVisible(extentRect, false, alignment, alignment);
- }
-}
-
-Frame* Frame::frameForWidget(const Widget* widget)
-{
- ASSERT_ARG(widget, widget);
-
- if (RenderWidget* renderer = RenderWidget::find(widget))
- if (Node* node = renderer->node())
- return node->document()->frame();
-
- // Assume all widgets are either a FrameView or owned by a RenderWidget.
- // FIXME: That assumption is not right for scroll bars!
- ASSERT(widget->isFrameView());
- return static_cast<const FrameView*>(widget)->frame();
-}
-
-void Frame::clearTimers(FrameView *view, Document *document)
-{
- if (view) {
- view->unscheduleRelayout();
- if (view->frame()) {
- if (document && document->renderer() && document->renderer()->hasLayer())
- document->renderView()->layer()->suspendMarquees();
- view->frame()->animation()->suspendAnimations(document);
- view->frame()->eventHandler()->stopAutoscrollTimer();
- }
- }
-}
-
-void Frame::clearTimers()
-{
- clearTimers(m_view.get(), document());
-}
-
-RenderStyle *Frame::styleForSelectionStart(Node *&nodeToRemove) const
-{
- nodeToRemove = 0;
-
- if (!document())
- return 0;
- if (selection()->isNone())
- return 0;
-
- Position pos = selection()->selection().visibleStart().deepEquivalent();
- if (!pos.isCandidate())
- return 0;
- Node *node = pos.node();
- if (!node)
- return 0;
-
- if (!m_typingStyle)
- return node->renderer()->style();
-
- ExceptionCode ec = 0;
- RefPtr<Element> styleElement = document()->createElementNS(xhtmlNamespaceURI, "span", ec);
- ASSERT(ec == 0);
-
- String styleText = m_typingStyle->cssText() + " display: inline";
- styleElement->setAttribute(styleAttr, styleText.impl(), ec);
- ASSERT(ec == 0);
-
- styleElement->appendChild(document()->createEditingTextNode(""), ec);
- ASSERT(ec == 0);
-
- node->parentNode()->appendChild(styleElement, ec);
- ASSERT(ec == 0);
-
- nodeToRemove = styleElement.get();
- return styleElement->renderer() ? styleElement->renderer()->style() : 0;
-}
-
-void Frame::setSelectionFromNone()
-{
- // Put a caret inside the body if the entire frame is editable (either the
- // entire WebView is editable or designMode is on for this document).
- Document *doc = document();
- if (!doc || !selection()->isNone() || !isContentEditable())
- return;
-
- Node* node = doc->documentElement();
- while (node && !node->hasTagName(bodyTag))
- node = node->traverseNextNode();
- if (node)
- selection()->setSelection(Selection(Position(node, 0), DOWNSTREAM));
-}
-
-bool Frame::inViewSourceMode() const
-{
- return m_inViewSourceMode;
-}
-
-void Frame::setInViewSourceMode(bool mode)
-{
- m_inViewSourceMode = mode;
-}
-
-// Searches from the beginning of the document if nothing is selected.
-bool Frame::findString(const String& target, bool forward, bool caseFlag, bool wrapFlag, bool startInSelection)
-{
- if (target.isEmpty() || !document())
- return false;
-
- if (excludeFromTextSearch())
- return false;
-
- // Start from an edge of the selection, if there's a selection that's not in shadow content. Which edge
- // is used depends on whether we're searching forward or backward, and whether startInSelection is set.
- RefPtr<Range> searchRange(rangeOfContents(document()));
- Selection selection = this->selection()->selection();
-
- if (forward)
- setStart(searchRange.get(), startInSelection ? selection.visibleStart() : selection.visibleEnd());
- else
- setEnd(searchRange.get(), startInSelection ? selection.visibleEnd() : selection.visibleStart());
-
- Node* shadowTreeRoot = selection.shadowTreeRootNode();
- if (shadowTreeRoot) {
- ExceptionCode ec = 0;
- if (forward)
- searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec);
- else
- searchRange->setStart(shadowTreeRoot, 0, ec);
- }
-
- RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, forward, caseFlag));
- // If we started in the selection and the found range exactly matches the existing selection, find again.
- // Build a selection with the found range to remove collapsed whitespace.
- // Compare ranges instead of selection objects to ignore the way that the current selection was made.
- if (startInSelection && *Selection(resultRange.get()).toRange() == *selection.toRange()) {
- searchRange = rangeOfContents(document());
- if (forward)
- setStart(searchRange.get(), selection.visibleEnd());
- else
- setEnd(searchRange.get(), selection.visibleStart());
-
- if (shadowTreeRoot) {
- ExceptionCode ec = 0;
- if (forward)
- searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec);
- else
- searchRange->setStart(shadowTreeRoot, 0, ec);
- }
-
- resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
- }
-
- ExceptionCode exception = 0;
-
- // If nothing was found in the shadow tree, search in main content following the shadow tree.
- if (resultRange->collapsed(exception) && shadowTreeRoot) {
- searchRange = rangeOfContents(document());
- if (forward)
- searchRange->setStartAfter(shadowTreeRoot->shadowParentNode(), exception);
- else
- searchRange->setEndBefore(shadowTreeRoot->shadowParentNode(), exception);
-
- resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
- }
-
- if (!editor()->insideVisibleArea(resultRange.get())) {
- resultRange = editor()->nextVisibleRange(resultRange.get(), target, forward, caseFlag, wrapFlag);
- if (!resultRange)
- return false;
- }
-
- // If we didn't find anything and we're wrapping, search again in the entire document (this will
- // redundantly re-search the area already searched in some cases).
- if (resultRange->collapsed(exception) && wrapFlag) {
- searchRange = rangeOfContents(document());
- resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
- // We used to return false here if we ended up with the same range that we started with
- // (e.g., the selection was already the only instance of this text). But we decided that
- // this should be a success case instead, so we'll just fall through in that case.
- }
-
- if (resultRange->collapsed(exception))
- return false;
-
- this->selection()->setSelection(Selection(resultRange.get(), DOWNSTREAM));
- revealSelection();
- return true;
-}
-
-unsigned Frame::markAllMatchesForText(const String& target, bool caseFlag, unsigned limit)
-{
- if (target.isEmpty() || !document())
- return 0;
-
- RefPtr<Range> searchRange(rangeOfContents(document()));
-
- ExceptionCode exception = 0;
- unsigned matchCount = 0;
- do {
- RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, true, caseFlag));
- if (resultRange->collapsed(exception)) {
- if (!resultRange->startContainer()->isInShadowTree())
- break;
-
- searchRange = rangeOfContents(document());
- searchRange->setStartAfter(resultRange->startContainer()->shadowAncestorNode(), exception);
- continue;
- }
-
- // A non-collapsed result range can in some funky whitespace cases still not
- // advance the range's start position (4509328). Break to avoid infinite loop.
- VisiblePosition newStart = endVisiblePosition(resultRange.get(), DOWNSTREAM);
- if (newStart == startVisiblePosition(searchRange.get(), DOWNSTREAM))
- break;
-
- // Only treat the result as a match if it is visible
- if (editor()->insideVisibleArea(resultRange.get())) {
- ++matchCount;
- document()->addMarker(resultRange.get(), DocumentMarker::TextMatch);
- }
-
- // Stop looking if we hit the specified limit. A limit of 0 means no limit.
- if (limit > 0 && matchCount >= limit)
- break;
-
- setStart(searchRange.get(), newStart);
- Node* shadowTreeRoot = searchRange->shadowTreeRootNode();
- if (searchRange->collapsed(exception) && shadowTreeRoot)
- searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), exception);
- } while (true);
-
- // Do a "fake" paint in order to execute the code that computes the rendered rect for
- // each text match.
- Document* doc = document();
- if (doc && m_view && contentRenderer()) {
- doc->updateLayout(); // Ensure layout is up to date.
- IntRect visibleRect = m_view->visibleContentRect();
- if (!visibleRect.isEmpty()) {
- GraphicsContext context((PlatformGraphicsContext*)0);
- context.setPaintingDisabled(true);
- m_view->paintContents(&context, visibleRect);
- }
- }
-
- return matchCount;
-}
-
-bool Frame::markedTextMatchesAreHighlighted() const
-{
- return m_highlightTextMatches;
-}
-
-void Frame::setMarkedTextMatchesAreHighlighted(bool flag)
-{
- if (flag == m_highlightTextMatches || !document())
- return;
-
- m_highlightTextMatches = flag;
- document()->repaintMarkers(DocumentMarker::TextMatch);
-}
-
-FrameTree* Frame::tree() const
-{
- return &m_treeNode;
-}
-
-void Frame::setDOMWindow(DOMWindow* domWindow)
-{
- if (m_domWindow) {
- m_liveFormerWindows.add(m_domWindow.get());
- m_domWindow->clear();
- }
- m_domWindow = domWindow;
-}
-
-DOMWindow* Frame::domWindow() const
-{
- if (!m_domWindow)
- m_domWindow = DOMWindow::create(const_cast<Frame*>(this));
-
- return m_domWindow.get();
-}
-
-void Frame::clearFormerDOMWindow(DOMWindow* window)
-{
- m_liveFormerWindows.remove(window);
-}
-
-Page* Frame::page() const
-{
- return m_page;
-}
-
-EventHandler* Frame::eventHandler() const
-{
- return &m_eventHandler;
-}
-
-void Frame::pageDestroyed()
-{
- if (Frame* parent = tree()->parent())
- parent->loader()->checkLoadComplete();
-
- // FIXME: It's unclear as to why this is called more than once, but it is,
- // so page() could be NULL.
- if (page() && page()->focusController()->focusedFrame() == this)
- page()->focusController()->setFocusedFrame(0);
-
-#if USE(JSC)
- // TODO(fqian): Unfork this change. It is a temporary workaround
- // for this merge to pass layout tests. Once the merge is landed
- // in the trunk, I am going to unfork this change and fix the issue
- // in the binding code.
- script()->clearWindowShell();
-#endif
-
- // This will stop any JS timers
-#if USE(JSC)
- if (script()->haveWindowShell())
- script()->windowShell()->disconnectFrame();
-#elif USE(V8)
- script()->disconnectFrame();
-#endif
-
- script()->clearScriptObjects();
- script()->updatePlatformScriptObjects();
-
- m_page = 0;
-}
-
-void Frame::disconnectOwnerElement()
-{
- if (m_ownerElement) {
- if (Document* doc = document())
- doc->clearAXObjectCache();
- m_ownerElement->m_contentFrame = 0;
- if (m_page)
- m_page->decrementFrameCount();
- }
- m_ownerElement = 0;
-}
-
-String Frame::documentTypeString() const
-{
- if (Document* doc = document()) {
- if (DocumentType* doctype = doc->doctype())
- return createMarkup(doctype);
- }
-
- return String();
-}
-
-void Frame::focusWindow()
-{
- if (!page())
- return;
-
- // If we're a top level window, bring the window to the front.
- if (!tree()->parent())
- page()->chrome()->focus();
-
- eventHandler()->focusDocumentView();
-}
-
-void Frame::unfocusWindow()
-{
- if (!page())
- return;
-
- // If we're a top level window, deactivate the window.
- if (!tree()->parent())
- page()->chrome()->unfocus();
-}
-
-bool Frame::shouldClose()
-{
- Chrome* chrome = page() ? page()->chrome() : 0;
- if (!chrome || !chrome->canRunBeforeUnloadConfirmPanel())
- return true;
-
- RefPtr<Document> doc = document();
- if (!doc)
- return true;
- HTMLElement* body = doc->body();
- if (!body)
- return true;
-
- loader()->setFiringUnloadEvents(true);
-
- RefPtr<BeforeUnloadEvent> beforeUnloadEvent = BeforeUnloadEvent::create();
- beforeUnloadEvent->setTarget(doc);
- doc->handleWindowEvent(beforeUnloadEvent.get(), false);
-
- if (!beforeUnloadEvent->defaultPrevented() && doc)
- doc->defaultEventHandler(beforeUnloadEvent.get());
-
- loader()->setFiringUnloadEvents(false);
-
- if (beforeUnloadEvent->result().isNull())
- return true;
-
- String text = doc->displayStringModifiedByEncoding(beforeUnloadEvent->result());
- return chrome->runBeforeUnloadConfirmPanel(text, this);
-}
-
-void Frame::scheduleClose()
-{
- if (!shouldClose())
- return;
-
- Chrome* chrome = page() ? page()->chrome() : 0;
- if (chrome)
- chrome->closeWindowSoon();
-}
-
-void Frame::respondToChangedSelection(const Selection& oldSelection, bool closeTyping)
-{
- if (document()) {
- bool isContinuousSpellCheckingEnabled = editor()->isContinuousSpellCheckingEnabled();
- bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled && editor()->isGrammarCheckingEnabled();
- if (isContinuousSpellCheckingEnabled) {
- Selection newAdjacentWords;
- Selection newSelectedSentence;
- if (selection()->selection().isContentEditable()) {
- VisiblePosition newStart(selection()->selection().visibleStart());
- newAdjacentWords = Selection(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
- if (isContinuousGrammarCheckingEnabled)
- newSelectedSentence = Selection(startOfSentence(newStart), endOfSentence(newStart));
- }
-
- // When typing we check spelling elsewhere, so don't redo it here.
- // If this is a change in selection resulting from a delete operation,
- // oldSelection may no longer be in the document.
- if (closeTyping && oldSelection.isContentEditable() && oldSelection.start().node() && oldSelection.start().node()->inDocument()) {
- VisiblePosition oldStart(oldSelection.visibleStart());
- Selection oldAdjacentWords = Selection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
- if (oldAdjacentWords != newAdjacentWords) {
- editor()->markMisspellings(oldAdjacentWords);
- if (isContinuousGrammarCheckingEnabled) {
- Selection oldSelectedSentence = Selection(startOfSentence(oldStart), endOfSentence(oldStart));
- if (oldSelectedSentence != newSelectedSentence)
- editor()->markBadGrammar(oldSelectedSentence);
- }
- }
- }
-
- // This only erases markers that are in the first unit (word or sentence) of the selection.
- // Perhaps peculiar, but it matches AppKit.
- if (RefPtr<Range> wordRange = newAdjacentWords.toRange())
- document()->removeMarkers(wordRange.get(), DocumentMarker::Spelling);
- if (RefPtr<Range> sentenceRange = newSelectedSentence.toRange())
- document()->removeMarkers(sentenceRange.get(), DocumentMarker::Grammar);
- }
-
- // When continuous spell checking is off, existing markers disappear after the selection changes.
- if (!isContinuousSpellCheckingEnabled)
- document()->removeMarkers(DocumentMarker::Spelling);
- if (!isContinuousGrammarCheckingEnabled)
- document()->removeMarkers(DocumentMarker::Grammar);
- }
-
- editor()->respondToChangedSelection(oldSelection);
-}
-
-VisiblePosition Frame::visiblePositionForPoint(const IntPoint& framePoint)
-{
- HitTestResult result = eventHandler()->hitTestResultAtPoint(framePoint, true);
- Node* node = result.innerNode();
- if (!node)
- return VisiblePosition();
- RenderObject* renderer = node->renderer();
- if (!renderer)
- return VisiblePosition();
- VisiblePosition visiblePos = renderer->positionForCoordinates(result.localPoint().x(), result.localPoint().y());
- if (visiblePos.isNull())
- visiblePos = VisiblePosition(Position(node, 0));
- return visiblePos;
-}
-
-Document* Frame::documentAtPoint(const IntPoint& point)
-{
- if (!view())
- return 0;
-
- IntPoint pt = view()->windowToContents(point);
- HitTestResult result = HitTestResult(pt);
-
- if (contentRenderer())
- result = eventHandler()->hitTestResultAtPoint(pt, false);
- return result.innerNode() ? result.innerNode()->document() : 0;
-}
-
-void Frame::createView(const IntSize& viewportSize,
- const Color& backgroundColor, bool transparent,
- const IntSize& fixedLayoutSize, bool useFixedLayout,
- ScrollbarMode horizontalScrollbarMode, ScrollbarMode verticalScrollbarMode)
-{
- ASSERT(this);
- ASSERT(m_page);
-
- bool isMainFrame = this == m_page->mainFrame();
-
- if (isMainFrame && view())
- view()->setParentVisible(false);
-
- setView(0);
-
- FrameView* frameView;
- if (isMainFrame) {
- frameView = new FrameView(this, viewportSize);
- frameView->setFixedLayoutSize(fixedLayoutSize);
- frameView->setUseFixedLayout(useFixedLayout);
- } else
- frameView = new FrameView(this);
-
- frameView->setScrollbarModes(horizontalScrollbarMode, verticalScrollbarMode);
- frameView->updateDefaultScrollbarState();
-
- setView(frameView);
- // FrameViews are created with a ref count of 1. Release this ref since we've assigned it to frame.
- frameView->deref();
-
- if (backgroundColor.isValid())
- frameView->updateBackgroundRecursively(backgroundColor, transparent);
-
- if (isMainFrame)
- frameView->setParentVisible(true);
-
- if (ownerRenderer())
- ownerRenderer()->setWidget(frameView);
-
- if (HTMLFrameOwnerElement* owner = ownerElement())
- view()->setCanHaveScrollbars(owner->scrollingMode() != ScrollbarAlwaysOff);
-}
-
-} // namespace WebCore
+/*
+ * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ * 1999 Lars Knoll <knoll@kde.org>
+ * 1999 Antti Koivisto <koivisto@kde.org>
+ * 2000 Simon Hausmann <hausmann@kde.org>
+ * 2000 Stefan Schimanski <1Stein@gmx.de>
+ * 2001 George Staikos <staikos@kde.org>
+ * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2005 Alexey Proskuryakov <ap@nypop.com>
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "Frame.h"
+
+#include "ApplyStyleCommand.h"
+#include "BeforeUnloadEvent.h"
+#include "CSSComputedStyleDeclaration.h"
+#include "CSSProperty.h"
+#include "CSSPropertyNames.h"
+#include "CachedCSSStyleSheet.h"
+#include "DOMWindow.h"
+#include "DocLoader.h"
+#include "DocumentType.h"
+#include "EditingText.h"
+#include "EditorClient.h"
+#include "EventNames.h"
+#include "FocusController.h"
+#include "FloatQuad.h"
+#include "FrameLoader.h"
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "HTMLDocument.h"
+#include "HTMLFormElement.h"
+#include "HTMLFrameElementBase.h"
+#include "HTMLFormControlElement.h"
+#include "HTMLNames.h"
+#include "HTMLTableCellElement.h"
+#include "HitTestResult.h"
+#include "Logging.h"
+#include "markup.h"
+#include "MediaFeatureNames.h"
+#include "Navigator.h"
+#include "NodeList.h"
+#include "Page.h"
+#include "RegularExpression.h"
+#include "RenderPart.h"
+#include "RenderTableCell.h"
+#include "RenderTextControl.h"
+#include "RenderTheme.h"
+#include "RenderView.h"
+#include "ScriptController.h"
+#include "Settings.h"
+#include "TextIterator.h"
+#include "TextResourceDecoder.h"
+#include "XMLNames.h"
+#include "ScriptController.h"
+#include "npruntime_impl.h"
+#include "visible_units.h"
+#include <wtf/RefCountedLeakCounter.h>
+#include <wtf/StdLibExtras.h>
+
+#if USE(JSC)
+#include "JSDOMWindowShell.h"
+#include "runtime_root.h"
+#endif
+
+#if FRAME_LOADS_USER_STYLESHEET
+#include "UserStyleSheetLoader.h"
+#endif
+
+#if ENABLE(SVG)
+#include "SVGDocument.h"
+#include "SVGDocumentExtensions.h"
+#include "SVGNames.h"
+#include "XLinkNames.h"
+#endif
+
+#if ENABLE(WML)
+#include "WMLNames.h"
+#endif
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+#ifndef NDEBUG
+static WTF::RefCountedLeakCounter frameCounter("Frame");
+#endif
+
+static inline Frame* parentFromOwnerElement(HTMLFrameOwnerElement* ownerElement)
+{
+ if (!ownerElement)
+ return 0;
+ return ownerElement->document()->frame();
+}
+
+Frame::Frame(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* frameLoaderClient)
+ : m_page(page)
+ , m_treeNode(this, parentFromOwnerElement(ownerElement))
+ , m_loader(this, frameLoaderClient)
+ , m_ownerElement(ownerElement)
+ , m_script(this)
+ , m_selectionGranularity(CharacterGranularity)
+ , m_selectionController(this)
+ , m_caretBlinkTimer(this, &Frame::caretBlinkTimerFired)
+ , m_editor(this)
+ , m_eventHandler(this)
+ , m_animationController(this)
+ , m_lifeSupportTimer(this, &Frame::lifeSupportTimerFired)
+ , m_caretVisible(false)
+ , m_caretPaint(true)
+ , m_highlightTextMatches(false)
+ , m_inViewSourceMode(false)
+ , m_needsReapplyStyles(false)
+ , m_isDisconnected(false)
+ , m_excludeFromTextSearch(false)
+#if FRAME_LOADS_USER_STYLESHEET
+ , m_userStyleSheetLoader(0)
+#endif
+{
+ Frame* parent = parentFromOwnerElement(ownerElement);
+ m_zoomFactor = parent ? parent->m_zoomFactor : 1.0f;
+
+ AtomicString::init();
+ HTMLNames::init();
+ QualifiedName::init();
+ MediaFeatureNames::init();
+
+#if ENABLE(SVG)
+ SVGNames::init();
+ XLinkNames::init();
+#endif
+
+#if ENABLE(WML)
+ WMLNames::init();
+#endif
+
+ XMLNames::init();
+
+ if (!ownerElement)
+ page->setMainFrame(this);
+ else {
+ page->incrementFrameCount();
+ // Make sure we will not end up with two frames referencing the same owner element.
+ ASSERT((!(ownerElement->m_contentFrame)) || (ownerElement->m_contentFrame->ownerElement() != ownerElement));
+ ownerElement->m_contentFrame = this;
+ }
+
+#ifndef NDEBUG
+ frameCounter.increment();
+#endif
+}
+
+Frame::~Frame()
+{
+ setView(0);
+ loader()->clearRecordedFormValues();
+ loader()->cancelAndClear();
+
+ // FIXME: We should not be doing all this work inside the destructor
+
+ ASSERT(!m_lifeSupportTimer.isActive());
+
+#ifndef NDEBUG
+ frameCounter.decrement();
+#endif
+
+#if USE(JSC)
+ if (m_script.haveWindowShell())
+ m_script.windowShell()->disconnectFrame();
+#elif USE(V8)
+ m_script.disconnectFrame();
+#endif
+
+ disconnectOwnerElement();
+
+ if (m_domWindow)
+ m_domWindow->disconnectFrame();
+
+ HashSet<DOMWindow*>::iterator end = m_liveFormerWindows.end();
+ for (HashSet<DOMWindow*>::iterator it = m_liveFormerWindows.begin(); it != end; ++it)
+ (*it)->disconnectFrame();
+
+ if (m_view) {
+ m_view->hide();
+ m_view->clearFrame();
+ }
+
+ ASSERT(!m_lifeSupportTimer.isActive());
+
+#if FRAME_LOADS_USER_STYLESHEET
+ delete m_userStyleSheetLoader;
+#endif
+}
+
+void Frame::init()
+{
+ m_loader.init();
+}
+
+FrameLoader* Frame::loader() const
+{
+ return &m_loader;
+}
+
+FrameView* Frame::view() const
+{
+ return m_view.get();
+}
+
+void Frame::setView(FrameView* view)
+{
+ // Detach the document now, so any onUnload handlers get run - if
+ // we wait until the view is destroyed, then things won't be
+ // hooked up enough for some JavaScript calls to work.
+ if (!view && m_doc && m_doc->attached() && !m_doc->inPageCache()) {
+ // FIXME: We don't call willRemove here. Why is that OK?
+ m_doc->detach();
+ if (m_view)
+ m_view->unscheduleRelayout();
+ }
+ eventHandler()->clear();
+
+ m_view = view;
+
+ // Only one form submission is allowed per view of a part.
+ // Since this part may be getting reused as a result of being
+ // pulled from the back/forward cache, reset this flag.
+ loader()->resetMultipleFormSubmissionProtection();
+}
+
+ScriptController* Frame::script()
+{
+ return &m_script;
+}
+
+Document* Frame::document() const
+{
+ return m_doc.get();
+}
+
+void Frame::setDocument(PassRefPtr<Document> newDoc)
+{
+ if (m_doc && m_doc->attached() && !m_doc->inPageCache()) {
+ // FIXME: We don't call willRemove here. Why is that OK?
+ m_doc->detach();
+ }
+
+ m_doc = newDoc;
+ if (m_doc && selection()->isFocusedAndActive())
+ setUseSecureKeyboardEntry(m_doc->useSecureKeyboardEntryWhenActive());
+
+ if (m_doc && !m_doc->attached())
+ m_doc->attach();
+
+ // Update the cached 'document' property, which is now stale.
+ m_script.updateDocument();
+}
+
+Settings* Frame::settings() const
+{
+ return m_page ? m_page->settings() : 0;
+}
+
+String Frame::selectedText() const
+{
+ return plainText(selection()->toNormalizedRange().get());
+}
+
+IntRect Frame::firstRectForRange(Range* range) const
+{
+ int extraWidthToEndOfLine = 0;
+ ExceptionCode ec = 0;
+ ASSERT(range->startContainer(ec));
+ ASSERT(range->endContainer(ec));
+
+ InlineBox* startInlineBox;
+ int startCaretOffset;
+ range->startPosition().getInlineBoxAndOffset(DOWNSTREAM, startInlineBox, startCaretOffset);
+
+ RenderObject* startRenderer = range->startContainer(ec)->renderer();
+ IntRect startCaretRect = startRenderer->localCaretRect(startInlineBox, startCaretOffset, &extraWidthToEndOfLine);
+ if (startCaretRect != IntRect())
+ startCaretRect = startRenderer->localToAbsoluteQuad(FloatRect(startCaretRect)).enclosingBoundingBox();
+
+ InlineBox* endInlineBox;
+ int endCaretOffset;
+ range->endPosition().getInlineBoxAndOffset(UPSTREAM, endInlineBox, endCaretOffset);
+
+ RenderObject* endRenderer = range->endContainer(ec)->renderer();
+ IntRect endCaretRect = endRenderer->localCaretRect(endInlineBox, endCaretOffset);
+ if (endCaretRect != IntRect())
+ endCaretRect = endRenderer->localToAbsoluteQuad(FloatRect(endCaretRect)).enclosingBoundingBox();
+
+ if (startCaretRect.y() == endCaretRect.y()) {
+ // start and end are on the same line
+ return IntRect(min(startCaretRect.x(), endCaretRect.x()),
+ startCaretRect.y(),
+ abs(endCaretRect.x() - startCaretRect.x()),
+ max(startCaretRect.height(), endCaretRect.height()));
+ }
+
+ // start and end aren't on the same line, so go from start to the end of its line
+ return IntRect(startCaretRect.x(),
+ startCaretRect.y(),
+ startCaretRect.width() + extraWidthToEndOfLine,
+ startCaretRect.height());
+}
+
+SelectionController* Frame::selection() const
+{
+ return &m_selectionController;
+}
+
+Editor* Frame::editor() const
+{
+ return &m_editor;
+}
+
+TextGranularity Frame::selectionGranularity() const
+{
+ return m_selectionGranularity;
+}
+
+void Frame::setSelectionGranularity(TextGranularity granularity)
+{
+ m_selectionGranularity = granularity;
+}
+
+SelectionController* Frame::dragCaretController() const
+{
+ return m_page->dragCaretController();
+}
+
+
+AnimationController* Frame::animation() const
+{
+ return &m_animationController;
+}
+
+static RegularExpression* createRegExpForLabels(const Vector<String>& labels)
+{
+ // REVIEW- version of this call in FrameMac.mm caches based on the NSArray ptrs being
+ // the same across calls. We can't do that.
+
+ DEFINE_STATIC_LOCAL(RegularExpression, wordRegExp, ("\\w", TextCaseSensitive));
+ String pattern("(");
+ unsigned int numLabels = labels.size();
+ unsigned int i;
+ for (i = 0; i < numLabels; i++) {
+ String label = labels[i];
+
+ bool startsWithWordChar = false;
+ bool endsWithWordChar = false;
+ if (label.length() != 0) {
+ startsWithWordChar = wordRegExp.match(label.substring(0, 1)) >= 0;
+ endsWithWordChar = wordRegExp.match(label.substring(label.length() - 1, 1)) >= 0;
+ }
+
+ if (i != 0)
+ pattern.append("|");
+ // Search for word boundaries only if label starts/ends with "word characters".
+ // If we always searched for word boundaries, this wouldn't work for languages
+ // such as Japanese.
+ if (startsWithWordChar) {
+ pattern.append("\\b");
+ }
+ pattern.append(label);
+ if (endsWithWordChar) {
+ pattern.append("\\b");
+ }
+ }
+ pattern.append(")");
+ return new RegularExpression(pattern, TextCaseInsensitive);
+}
+
+String Frame::searchForLabelsAboveCell(RegularExpression* regExp, HTMLTableCellElement* cell)
+{
+ RenderTableCell* cellRenderer = static_cast<RenderTableCell*>(cell->renderer());
+
+ if (cellRenderer && cellRenderer->isTableCell()) {
+ RenderTableCell* cellAboveRenderer = cellRenderer->table()->cellAbove(cellRenderer);
+
+ if (cellAboveRenderer) {
+ HTMLTableCellElement* aboveCell =
+ static_cast<HTMLTableCellElement*>(cellAboveRenderer->element());
+
+ if (aboveCell) {
+ // search within the above cell we found for a match
+ for (Node* n = aboveCell->firstChild(); n; n = n->traverseNextNode(aboveCell)) {
+ if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
+ // For each text chunk, run the regexp
+ String nodeString = n->nodeValue();
+ int pos = regExp->searchRev(nodeString);
+ if (pos >= 0)
+ return nodeString.substring(pos, regExp->matchedLength());
+ }
+ }
+ }
+ }
+ }
+ // Any reason in practice to search all cells in that are above cell?
+ return String();
+}
+
+String Frame::searchForLabelsBeforeElement(const Vector<String>& labels, Element* element)
+{
+ OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels));
+ // We stop searching after we've seen this many chars
+ const unsigned int charsSearchedThreshold = 500;
+ // This is the absolute max we search. We allow a little more slop than
+ // charsSearchedThreshold, to make it more likely that we'll search whole nodes.
+ const unsigned int maxCharsSearched = 600;
+ // If the starting element is within a table, the cell that contains it
+ HTMLTableCellElement* startingTableCell = 0;
+ bool searchedCellAbove = false;
+
+ // walk backwards in the node tree, until another element, or form, or end of tree
+ int unsigned lengthSearched = 0;
+ Node* n;
+ for (n = element->traversePreviousNode();
+ n && lengthSearched < charsSearchedThreshold;
+ n = n->traversePreviousNode())
+ {
+ if (n->hasTagName(formTag)
+ || (n->isHTMLElement() && static_cast<Element*>(n)->isFormControlElement()))
+ {
+ // We hit another form element or the start of the form - bail out
+ break;
+ } else if (n->hasTagName(tdTag) && !startingTableCell) {
+ startingTableCell = static_cast<HTMLTableCellElement*>(n);
+ } else if (n->hasTagName(trTag) && startingTableCell) {
+ String result = searchForLabelsAboveCell(regExp.get(), startingTableCell);
+ if (!result.isEmpty())
+ return result;
+ searchedCellAbove = true;
+ } else if (n->isTextNode() && n->renderer() && n->renderer()->style()->visibility() == VISIBLE) {
+ // For each text chunk, run the regexp
+ String nodeString = n->nodeValue();
+ // add 100 for slop, to make it more likely that we'll search whole nodes
+ if (lengthSearched + nodeString.length() > maxCharsSearched)
+ nodeString = nodeString.right(charsSearchedThreshold - lengthSearched);
+ int pos = regExp->searchRev(nodeString);
+ if (pos >= 0)
+ return nodeString.substring(pos, regExp->matchedLength());
+ lengthSearched += nodeString.length();
+ }
+ }
+
+ // If we started in a cell, but bailed because we found the start of the form or the
+ // previous element, we still might need to search the row above us for a label.
+ if (startingTableCell && !searchedCellAbove) {
+ return searchForLabelsAboveCell(regExp.get(), startingTableCell);
+ }
+ return String();
+}
+
+String Frame::matchLabelsAgainstElement(const Vector<String>& labels, Element* element)
+{
+ String name = element->getAttribute(nameAttr);
+ if (name.isEmpty())
+ return String();
+
+ // Make numbers and _'s in field names behave like word boundaries, e.g., "address2"
+ replace(name, RegularExpression("\\d", TextCaseSensitive), " ");
+ name.replace('_', ' ');
+
+ OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels));
+ // Use the largest match we can find in the whole name string
+ int pos;
+ int length;
+ int bestPos = -1;
+ int bestLength = -1;
+ int start = 0;
+ do {
+ pos = regExp->match(name, start);
+ if (pos != -1) {
+ length = regExp->matchedLength();
+ if (length >= bestLength) {
+ bestPos = pos;
+ bestLength = length;
+ }
+ start = pos + 1;
+ }
+ } while (pos != -1);
+
+ if (bestPos != -1)
+ return name.substring(bestPos, bestLength);
+ return String();
+}
+
+const Selection& Frame::mark() const
+{
+ return m_mark;
+}
+
+void Frame::setMark(const Selection& s)
+{
+ ASSERT(!s.base().node() || s.base().node()->document() == document());
+ ASSERT(!s.extent().node() || s.extent().node()->document() == document());
+ ASSERT(!s.start().node() || s.start().node()->document() == document());
+ ASSERT(!s.end().node() || s.end().node()->document() == document());
+
+ m_mark = s;
+}
+
+void Frame::notifyRendererOfSelectionChange(bool userTriggered)
+{
+ RenderObject* renderer = 0;
+ if (selection()->rootEditableElement())
+ renderer = selection()->rootEditableElement()->shadowAncestorNode()->renderer();
+
+ // If the current selection is in a textfield or textarea, notify the renderer that the selection has changed
+ if (renderer && (renderer->isTextArea() || renderer->isTextField()))
+ static_cast<RenderTextControl*>(renderer)->selectionChanged(userTriggered);
+}
+
+void Frame::invalidateSelection()
+{
+ selection()->setNeedsLayout();
+ selectionLayoutChanged();
+}
+
+void Frame::setCaretVisible(bool flag)
+{
+ if (m_caretVisible == flag)
+ return;
+ clearCaretRectIfNeeded();
+ m_caretVisible = flag;
+ selectionLayoutChanged();
+}
+
+void Frame::clearCaretRectIfNeeded()
+{
+#if ENABLE(TEXT_CARET)
+ if (m_caretPaint) {
+ m_caretPaint = false;
+ selection()->invalidateCaretRect();
+ }
+#endif
+}
+
+// Helper function that tells whether a particular node is an element that has an entire
+// Frame and FrameView, a <frame>, <iframe>, or <object>.
+static bool isFrameElement(const Node *n)
+{
+ if (!n)
+ return false;
+ RenderObject *renderer = n->renderer();
+ if (!renderer || !renderer->isWidget())
+ return false;
+ Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
+ return widget && widget->isFrameView();
+}
+
+void Frame::setFocusedNodeIfNeeded()
+{
+ if (!document() || selection()->isNone() || !selection()->isFocusedAndActive())
+ return;
+
+ Node* target = selection()->rootEditableElement();
+ if (target) {
+ RenderObject* renderer = target->renderer();
+
+ // Walk up the render tree to search for a node to focus.
+ // Walking up the DOM tree wouldn't work for shadow trees, like those behind the engine-based text fields.
+ while (renderer) {
+ // We don't want to set focus on a subframe when selecting in a parent frame,
+ // so add the !isFrameElement check here. There's probably a better way to make this
+ // work in the long term, but this is the safest fix at this time.
+ if (target && target->isMouseFocusable() && !isFrameElement(target)) {
+ page()->focusController()->setFocusedNode(target, this);
+ return;
+ }
+ renderer = renderer->parent();
+ if (renderer)
+ target = renderer->element();
+ }
+ document()->setFocusedNode(0);
+ }
+}
+
+void Frame::selectionLayoutChanged()
+{
+ bool caretRectChanged = selection()->recomputeCaretRect();
+
+#if ENABLE(TEXT_CARET)
+ bool shouldBlink = m_caretVisible
+ && selection()->isCaret() && selection()->isContentEditable();
+
+ // If the caret moved, stop the blink timer so we can restart with a
+ // black caret in the new location.
+ if (caretRectChanged || !shouldBlink)
+ m_caretBlinkTimer.stop();
+
+ // Start blinking with a black caret. Be sure not to restart if we're
+ // already blinking in the right location.
+ if (shouldBlink && !m_caretBlinkTimer.isActive()) {
+ if (double blinkInterval = theme()->caretBlinkInterval())
+ m_caretBlinkTimer.startRepeating(blinkInterval);
+
+ if (!m_caretPaint) {
+ m_caretPaint = true;
+ selection()->invalidateCaretRect();
+ }
+ }
+#else
+ if (!caretRectChanged)
+ return;
+#endif
+
+ RenderView* view = contentRenderer();
+ if (!view)
+ return;
+
+ Selection selection = this->selection()->selection();
+
+ if (!selection.isRange())
+ view->clearSelection();
+ else {
+ // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection.
+ // Example: foo <a>bar</a>. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3]
+ // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected
+ // and will fill the gap before 'bar'.
+ Position startPos = selection.start();
+ if (startPos.downstream().isCandidate())
+ startPos = startPos.downstream();
+ Position endPos = selection.end();
+ if (endPos.upstream().isCandidate())
+ endPos = endPos.upstream();
+
+ // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted
+ // because we don't yet notify the SelectionController of text removal.
+ if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
+ RenderObject *startRenderer = startPos.node()->renderer();
+ RenderObject *endRenderer = endPos.node()->renderer();
+ view->setSelection(startRenderer, startPos.offset(), endRenderer, endPos.offset());
+ }
+ }
+}
+
+void Frame::caretBlinkTimerFired(Timer<Frame>*)
+{
+#if ENABLE(TEXT_CARET)
+ ASSERT(m_caretVisible);
+ ASSERT(selection()->isCaret());
+ bool caretPaint = m_caretPaint;
+ if (selection()->isCaretBlinkingSuspended() && caretPaint)
+ return;
+ m_caretPaint = !caretPaint;
+ selection()->invalidateCaretRect();
+#endif
+}
+
+void Frame::paintCaret(GraphicsContext* p, int tx, int ty, const IntRect& clipRect) const
+{
+#if ENABLE(TEXT_CARET)
+ if (m_caretPaint && m_caretVisible)
+ selection()->paintCaret(p, tx, ty, clipRect);
+#endif
+}
+
+void Frame::paintDragCaret(GraphicsContext* p, int tx, int ty, const IntRect& clipRect) const
+{
+#if ENABLE(TEXT_CARET)
+ SelectionController* dragCaretController = m_page->dragCaretController();
+ ASSERT(dragCaretController->selection().isCaret());
+ if (dragCaretController->selection().start().node()->document()->frame() == this)
+ dragCaretController->paintCaret(p, tx, ty, clipRect);
+#endif
+}
+
+float Frame::zoomFactor() const
+{
+ return m_zoomFactor;
+}
+
+bool Frame::isZoomFactorTextOnly() const
+{
+ return m_page->settings()->zoomsTextOnly();
+}
+
+bool Frame::shouldApplyTextZoom() const
+{
+ if (m_zoomFactor == 1.0f || !isZoomFactorTextOnly())
+ return false;
+#if ENABLE(SVG)
+ if (m_doc && m_doc->isSVGDocument())
+ return false;
+#endif
+ return true;
+}
+
+bool Frame::shouldApplyPageZoom() const
+{
+ if (m_zoomFactor == 1.0f || isZoomFactorTextOnly())
+ return false;
+#if ENABLE(SVG)
+ if (m_doc && m_doc->isSVGDocument())
+ return false;
+#endif
+ return true;
+}
+
+void Frame::setZoomFactor(float percent, bool isTextOnly)
+{
+ if (m_zoomFactor == percent && isZoomFactorTextOnly() == isTextOnly)
+ return;
+
+#if ENABLE(SVG)
+ // SVG doesn't care if the zoom factor is text only. It will always apply a
+ // zoom to the whole SVG.
+ if (m_doc && m_doc->isSVGDocument()) {
+ if (!static_cast<SVGDocument*>(m_doc.get())->zoomAndPanEnabled())
+ return;
+ m_zoomFactor = percent;
+ m_page->settings()->setZoomsTextOnly(true); // We do this to avoid doing any scaling of CSS pixels, since the SVG has its own notion of zoom.
+ if (m_doc->renderer())
+ m_doc->renderer()->repaint();
+ return;
+ }
+#endif
+
+ m_zoomFactor = percent;
+ m_page->settings()->setZoomsTextOnly(isTextOnly);
+
+ if (m_doc)
+ m_doc->recalcStyle(Node::Force);
+
+ for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling())
+ child->setZoomFactor(m_zoomFactor, isTextOnly);
+
+ if (m_doc && m_doc->renderer() && m_doc->renderer()->needsLayout() && view()->didFirstLayout())
+ view()->layout();
+}
+
+void Frame::setPrinting(bool printing, float minPageWidth, float maxPageWidth, bool adjustViewSize)
+{
+ if (!m_doc)
+ return;
+
+ m_doc->setPrinting(printing);
+ view()->setMediaType(printing ? "print" : "screen");
+ m_doc->updateStyleSelector();
+ view()->forceLayoutWithPageWidthRange(minPageWidth, maxPageWidth, adjustViewSize);
+
+ for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling())
+ child->setPrinting(printing, minPageWidth, maxPageWidth, adjustViewSize);
+}
+
+void Frame::setJSStatusBarText(const String& text)
+{
+ m_kjsStatusBarText = text;
+ if (m_page)
+ m_page->chrome()->setStatusbarText(this, m_kjsStatusBarText);
+}
+
+void Frame::setJSDefaultStatusBarText(const String& text)
+{
+ m_kjsDefaultStatusBarText = text;
+ if (m_page)
+ m_page->chrome()->setStatusbarText(this, m_kjsDefaultStatusBarText);
+}
+
+String Frame::jsStatusBarText() const
+{
+ return m_kjsStatusBarText;
+}
+
+String Frame::jsDefaultStatusBarText() const
+{
+ return m_kjsDefaultStatusBarText;
+}
+
+void Frame::setNeedsReapplyStyles()
+{
+ if (m_needsReapplyStyles)
+ return;
+
+ m_needsReapplyStyles = true;
+
+ // FrameView's "layout" timer includes reapplyStyles, so despite its
+ // name, it's what we want to call here.
+ if (view())
+ view()->scheduleRelayout();
+}
+
+bool Frame::needsReapplyStyles() const
+{
+ return m_needsReapplyStyles;
+}
+
+void Frame::reapplyStyles()
+{
+ m_needsReapplyStyles = false;
+
+ // FIXME: This call doesn't really make sense in a method called
+ // "reapplyStyles". We should probably eventually move it into its own
+ // method.
+ if (m_doc)
+ m_doc->docLoader()->setAutoLoadImages(m_page && m_page->settings()->loadsImagesAutomatically());
+
+#if FRAME_LOADS_USER_STYLESHEET
+ const KURL userStyleSheetLocation = m_page ? m_page->settings()->userStyleSheetLocation() : KURL();
+ if (!userStyleSheetLocation.isEmpty())
+ setUserStyleSheetLocation(userStyleSheetLocation);
+ else
+ setUserStyleSheet(String());
+#endif
+
+ // FIXME: It's not entirely clear why the following is needed.
+ // The document automatically does this as required when you set the style sheet.
+ // But we had problems when this code was removed. Details are in
+ // <http://bugs.webkit.org/show_bug.cgi?id=8079>.
+ if (m_doc)
+ m_doc->updateStyleSelector();
+}
+
+bool Frame::shouldChangeSelection(const Selection& newSelection) const
+{
+ return shouldChangeSelection(selection()->selection(), newSelection, newSelection.affinity(), false);
+}
+
+bool Frame::shouldChangeSelection(const Selection& oldSelection, const Selection& newSelection, EAffinity affinity, bool stillSelecting) const
+{
+ return editor()->client()->shouldChangeSelectedRange(oldSelection.toNormalizedRange().get(), newSelection.toNormalizedRange().get(),
+ affinity, stillSelecting);
+}
+
+bool Frame::shouldDeleteSelection(const Selection& selection) const
+{
+ return editor()->client()->shouldDeleteRange(selection.toNormalizedRange().get());
+}
+
+bool Frame::isContentEditable() const
+{
+ if (m_editor.clientIsEditable())
+ return true;
+ if (!m_doc)
+ return false;
+ return m_doc->inDesignMode();
+}
+
+#if !PLATFORM(MAC)
+
+void Frame::setUseSecureKeyboardEntry(bool)
+{
+}
+
+#endif
+
+void Frame::updateSecureKeyboardEntryIfActive()
+{
+ if (selection()->isFocusedAndActive())
+ setUseSecureKeyboardEntry(m_doc->useSecureKeyboardEntryWhenActive());
+}
+
+CSSMutableStyleDeclaration *Frame::typingStyle() const
+{
+ return m_typingStyle.get();
+}
+
+void Frame::setTypingStyle(CSSMutableStyleDeclaration *style)
+{
+ m_typingStyle = style;
+}
+
+void Frame::clearTypingStyle()
+{
+ m_typingStyle = 0;
+}
+
+void Frame::computeAndSetTypingStyle(CSSStyleDeclaration *style, EditAction editingAction)
+{
+ if (!style || style->length() == 0) {
+ clearTypingStyle();
+ return;
+ }
+
+ // Calculate the current typing style.
+ RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable();
+ if (typingStyle()) {
+ typingStyle()->merge(mutableStyle.get());
+ mutableStyle = typingStyle();
+ }
+
+ RefPtr<CSSValue> unicodeBidi;
+ RefPtr<CSSValue> direction;
+ if (editingAction == EditActionSetWritingDirection) {
+ unicodeBidi = mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
+ direction = mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
+ }
+
+ Node* node = selection()->selection().visibleStart().deepEquivalent().node();
+ computedStyle(node)->diff(mutableStyle.get());
+
+ if (editingAction == EditActionSetWritingDirection && unicodeBidi) {
+ ASSERT(unicodeBidi->isPrimitiveValue());
+ mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent());
+ if (direction) {
+ ASSERT(direction->isPrimitiveValue());
+ mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent());
+ }
+ }
+
+ // Handle block styles, substracting these from the typing style.
+ RefPtr<CSSMutableStyleDeclaration> blockStyle = mutableStyle->copyBlockProperties();
+ blockStyle->diff(mutableStyle.get());
+ if (document() && blockStyle->length() > 0)
+ applyCommand(ApplyStyleCommand::create(document(), blockStyle.get(), editingAction));
+
+ // Set the remaining style as the typing style.
+ m_typingStyle = mutableStyle.release();
+}
+
+String Frame::selectionStartStylePropertyValue(int stylePropertyID) const
+{
+ Node *nodeToRemove;
+ RefPtr<CSSStyleDeclaration> selectionStyle = selectionComputedStyle(nodeToRemove);
+ if (!selectionStyle)
+ return String();
+
+ String value = selectionStyle->getPropertyValue(stylePropertyID);
+
+ if (nodeToRemove) {
+ ExceptionCode ec = 0;
+ nodeToRemove->remove(ec);
+ ASSERT(ec == 0);
+ }
+
+ return value;
+}
+
+PassRefPtr<CSSComputedStyleDeclaration> Frame::selectionComputedStyle(Node*& nodeToRemove) const
+{
+ nodeToRemove = 0;
+
+ if (!document())
+ return 0;
+
+ if (selection()->isNone())
+ return 0;
+
+ RefPtr<Range> range(selection()->toNormalizedRange());
+ Position pos = range->editingStartPosition();
+
+ Element *elem = pos.element();
+ if (!elem)
+ return 0;
+
+ RefPtr<Element> styleElement = elem;
+ ExceptionCode ec = 0;
+
+ if (m_typingStyle) {
+ styleElement = document()->createElementNS(xhtmlNamespaceURI, "span", ec);
+ ASSERT(ec == 0);
+
+ styleElement->setAttribute(styleAttr, m_typingStyle->cssText().impl(), ec);
+ ASSERT(ec == 0);
+
+ styleElement->appendChild(document()->createEditingTextNode(""), ec);
+ ASSERT(ec == 0);
+
+ if (elem->renderer() && elem->renderer()->canHaveChildren()) {
+ elem->appendChild(styleElement, ec);
+ } else {
+ Node *parent = elem->parent();
+ Node *next = elem->nextSibling();
+
+ if (next) {
+ parent->insertBefore(styleElement, next, ec);
+ } else {
+ parent->appendChild(styleElement, ec);
+ }
+ }
+ ASSERT(ec == 0);
+
+ nodeToRemove = styleElement.get();
+ }
+
+ return computedStyle(styleElement.release());
+}
+
+void Frame::textFieldDidBeginEditing(Element* e)
+{
+ if (editor()->client())
+ editor()->client()->textFieldDidBeginEditing(e);
+}
+
+void Frame::textFieldDidEndEditing(Element* e)
+{
+ if (editor()->client())
+ editor()->client()->textFieldDidEndEditing(e);
+}
+
+void Frame::textDidChangeInTextField(Element* e)
+{
+ if (editor()->client())
+ editor()->client()->textDidChangeInTextField(e);
+}
+
+bool Frame::doTextFieldCommandFromEvent(Element* e, KeyboardEvent* ke)
+{
+ if (editor()->client())
+ return editor()->client()->doTextFieldCommandFromEvent(e, ke);
+
+ return false;
+}
+
+void Frame::textWillBeDeletedInTextField(Element* input)
+{
+ if (editor()->client())
+ editor()->client()->textWillBeDeletedInTextField(input);
+}
+
+void Frame::textDidChangeInTextArea(Element* e)
+{
+ if (editor()->client())
+ editor()->client()->textDidChangeInTextArea(e);
+}
+
+void Frame::applyEditingStyleToBodyElement() const
+{
+ if (!m_doc)
+ return;
+
+ RefPtr<NodeList> list = m_doc->getElementsByTagName("body");
+ unsigned len = list->length();
+ for (unsigned i = 0; i < len; i++) {
+ applyEditingStyleToElement(static_cast<Element*>(list->item(i)));
+ }
+}
+
+void Frame::removeEditingStyleFromBodyElement() const
+{
+ if (!m_doc)
+ return;
+
+ RefPtr<NodeList> list = m_doc->getElementsByTagName("body");
+ unsigned len = list->length();
+ for (unsigned i = 0; i < len; i++) {
+ removeEditingStyleFromElement(static_cast<Element*>(list->item(i)));
+ }
+}
+
+void Frame::applyEditingStyleToElement(Element* element) const
+{
+ if (!element)
+ return;
+
+ CSSStyleDeclaration* style = element->style();
+ ASSERT(style);
+
+ ExceptionCode ec = 0;
+ style->setProperty(CSSPropertyWordWrap, "break-word", false, ec);
+ ASSERT(ec == 0);
+ style->setProperty(CSSPropertyWebkitNbspMode, "space", false, ec);
+ ASSERT(ec == 0);
+ style->setProperty(CSSPropertyWebkitLineBreak, "after-white-space", false, ec);
+ ASSERT(ec == 0);
+}
+
+void Frame::removeEditingStyleFromElement(Element*) const
+{
+}
+
+#ifndef NDEBUG
+static HashSet<Frame*>& keepAliveSet()
+{
+ DEFINE_STATIC_LOCAL(HashSet<Frame*>, staticKeepAliveSet, ());
+ return staticKeepAliveSet;
+}
+#endif
+
+void Frame::keepAlive()
+{
+ if (m_lifeSupportTimer.isActive())
+ return;
+#ifndef NDEBUG
+ keepAliveSet().add(this);
+#endif
+ ref();
+ m_lifeSupportTimer.startOneShot(0);
+}
+
+#ifndef NDEBUG
+void Frame::cancelAllKeepAlive()
+{
+ HashSet<Frame*>::iterator end = keepAliveSet().end();
+ for (HashSet<Frame*>::iterator it = keepAliveSet().begin(); it != end; ++it) {
+ Frame* frame = *it;
+ frame->m_lifeSupportTimer.stop();
+ frame->deref();
+ }
+ keepAliveSet().clear();
+}
+#endif
+
+void Frame::lifeSupportTimerFired(Timer<Frame>*)
+{
+#ifndef NDEBUG
+ keepAliveSet().remove(this);
+#endif
+ deref();
+}
+
+void Frame::clearDOMWindow()
+{
+ if (m_domWindow) {
+ m_liveFormerWindows.add(m_domWindow.get());
+ m_domWindow->clear();
+ }
+ m_domWindow = 0;
+#if USE(V8)
+ m_script.clearPluginObjects();
+#endif
+}
+
+RenderView* Frame::contentRenderer() const
+{
+ Document* doc = document();
+ if (!doc)
+ return 0;
+ RenderObject* object = doc->renderer();
+ if (!object)
+ return 0;
+ ASSERT(object->isRenderView());
+ return toRenderView(object);
+}
+
+HTMLFrameOwnerElement* Frame::ownerElement() const
+{
+ return m_ownerElement;
+}
+
+RenderPart* Frame::ownerRenderer() const
+{
+ HTMLFrameOwnerElement* ownerElement = m_ownerElement;
+ if (!ownerElement)
+ return 0;
+ RenderObject* object = ownerElement->renderer();
+ if (!object)
+ return 0;
+ // FIXME: If <object> is ever fixed to disassociate itself from frames
+ // that it has started but canceled, then this can turn into an ASSERT
+ // since m_ownerElement would be 0 when the load is canceled.
+ // https://bugs.webkit.org/show_bug.cgi?id=18585
+ if (!object->isRenderPart())
+ return 0;
+ return static_cast<RenderPart*>(object);
+}
+
+bool Frame::isDisconnected() const
+{
+ return m_isDisconnected;
+}
+
+void Frame::setIsDisconnected(bool isDisconnected)
+{
+ m_isDisconnected = isDisconnected;
+}
+
+bool Frame::excludeFromTextSearch() const
+{
+ return m_excludeFromTextSearch;
+}
+
+void Frame::setExcludeFromTextSearch(bool exclude)
+{
+ m_excludeFromTextSearch = exclude;
+}
+
+// returns FloatRect because going through IntRect would truncate any floats
+FloatRect Frame::selectionBounds(bool clipToVisibleContent) const
+{
+ RenderView* root = contentRenderer();
+ FrameView* view = m_view.get();
+ if (!root || !view)
+ return IntRect();
+
+ IntRect selectionRect = root->selectionBounds(clipToVisibleContent);
+ return clipToVisibleContent ? intersection(selectionRect, view->visibleContentRect()) : selectionRect;
+}
+
+void Frame::selectionTextRects(Vector<FloatRect>& rects, bool clipToVisibleContent) const
+{
+ RenderView* root = contentRenderer();
+ if (!root)
+ return;
+
+ RefPtr<Range> selectedRange = selection()->toNormalizedRange();
+
+ Vector<IntRect> intRects;
+ selectedRange->addLineBoxRects(intRects, true);
+
+ unsigned size = intRects.size();
+ FloatRect visibleContentRect = m_view->visibleContentRect();
+ for (unsigned i = 0; i < size; ++i)
+ if (clipToVisibleContent)
+ rects.append(intersection(intRects[i], visibleContentRect));
+ else
+ rects.append(intRects[i]);
+}
+
+
+// Scans logically forward from "start", including any child frames
+static HTMLFormElement *scanForForm(Node *start)
+{
+ Node *n;
+ for (n = start; n; n = n->traverseNextNode()) {
+ if (n->hasTagName(formTag))
+ return static_cast<HTMLFormElement*>(n);
+ else if (n->isHTMLElement() && static_cast<Element*>(n)->isFormControlElement())
+ return static_cast<HTMLFormControlElement*>(n)->form();
+ else if (n->hasTagName(frameTag) || n->hasTagName(iframeTag)) {
+ Node *childDoc = static_cast<HTMLFrameElementBase*>(n)->contentDocument();
+ if (HTMLFormElement *frameResult = scanForForm(childDoc))
+ return frameResult;
+ }
+ }
+ return 0;
+}
+
+// We look for either the form containing the current focus, or for one immediately after it
+HTMLFormElement *Frame::currentForm() const
+{
+ // start looking either at the active (first responder) node, or where the selection is
+ Node *start = m_doc ? m_doc->focusedNode() : 0;
+ if (!start)
+ start = selection()->start().node();
+
+ // try walking up the node tree to find a form element
+ Node *n;
+ for (n = start; n; n = n->parentNode()) {
+ if (n->hasTagName(formTag))
+ return static_cast<HTMLFormElement*>(n);
+ else if (n->isHTMLElement() && static_cast<Element*>(n)->isFormControlElement())
+ return static_cast<HTMLFormControlElement*>(n)->form();
+ }
+
+ // try walking forward in the node tree to find a form element
+ return start ? scanForForm(start) : 0;
+}
+
+void Frame::revealSelection(const RenderLayer::ScrollAlignment& alignment) const
+{
+ IntRect rect;
+
+ switch (selection()->selectionType()) {
+ case Selection::NoSelection:
+ return;
+ case Selection::CaretSelection:
+ rect = selection()->absoluteCaretBounds();
+ break;
+ case Selection::RangeSelection:
+ rect = enclosingIntRect(selectionBounds(false));
+ break;
+ }
+
+ Position start = selection()->start();
+ ASSERT(start.node());
+ if (start.node() && start.node()->renderer()) {
+ // FIXME: This code only handles scrolling the startContainer's layer, but
+ // the selection rect could intersect more than just that.
+ // See <rdar://problem/4799899>.
+ if (RenderLayer* layer = start.node()->renderer()->enclosingLayer())
+ layer->scrollRectToVisible(rect, false, alignment, alignment);
+ }
+}
+
+void Frame::revealCaret(const RenderLayer::ScrollAlignment& alignment) const
+{
+ if (selection()->isNone())
+ return;
+
+ Position extent = selection()->extent();
+ if (extent.node() && extent.node()->renderer()) {
+ IntRect extentRect = VisiblePosition(extent).absoluteCaretBounds();
+ RenderLayer* layer = extent.node()->renderer()->enclosingLayer();
+ if (layer)
+ layer->scrollRectToVisible(extentRect, false, alignment, alignment);
+ }
+}
+
+Frame* Frame::frameForWidget(const Widget* widget)
+{
+ ASSERT_ARG(widget, widget);
+
+ if (RenderWidget* renderer = RenderWidget::find(widget))
+ if (Node* node = renderer->node())
+ return node->document()->frame();
+
+ // Assume all widgets are either a FrameView or owned by a RenderWidget.
+ // FIXME: That assumption is not right for scroll bars!
+ ASSERT(widget->isFrameView());
+ return static_cast<const FrameView*>(widget)->frame();
+}
+
+void Frame::clearTimers(FrameView *view, Document *document)
+{
+ if (view) {
+ view->unscheduleRelayout();
+ if (view->frame()) {
+ if (document && document->renderer() && document->renderer()->hasLayer())
+ document->renderView()->layer()->suspendMarquees();
+ view->frame()->animation()->suspendAnimations(document);
+ view->frame()->eventHandler()->stopAutoscrollTimer();
+ }
+ }
+}
+
+void Frame::clearTimers()
+{
+ clearTimers(m_view.get(), document());
+}
+
+RenderStyle *Frame::styleForSelectionStart(Node *&nodeToRemove) const
+{
+ nodeToRemove = 0;
+
+ if (!document())
+ return 0;
+ if (selection()->isNone())
+ return 0;
+
+ Position pos = selection()->selection().visibleStart().deepEquivalent();
+ if (!pos.isCandidate())
+ return 0;
+ Node *node = pos.node();
+ if (!node)
+ return 0;
+
+ if (!m_typingStyle)
+ return node->renderer()->style();
+
+ ExceptionCode ec = 0;
+ RefPtr<Element> styleElement = document()->createElementNS(xhtmlNamespaceURI, "span", ec);
+ ASSERT(ec == 0);
+
+ String styleText = m_typingStyle->cssText() + " display: inline";
+ styleElement->setAttribute(styleAttr, styleText.impl(), ec);
+ ASSERT(ec == 0);
+
+ styleElement->appendChild(document()->createEditingTextNode(""), ec);
+ ASSERT(ec == 0);
+
+ node->parentNode()->appendChild(styleElement, ec);
+ ASSERT(ec == 0);
+
+ nodeToRemove = styleElement.get();
+ return styleElement->renderer() ? styleElement->renderer()->style() : 0;
+}
+
+void Frame::setSelectionFromNone()
+{
+ // Put a caret inside the body if the entire frame is editable (either the
+ // entire WebView is editable or designMode is on for this document).
+ Document *doc = document();
+ if (!doc || !selection()->isNone() || !isContentEditable())
+ return;
+
+ Node* node = doc->documentElement();
+ while (node && !node->hasTagName(bodyTag))
+ node = node->traverseNextNode();
+ if (node)
+ selection()->setSelection(Selection(Position(node, 0), DOWNSTREAM));
+}
+
+bool Frame::inViewSourceMode() const
+{
+ return m_inViewSourceMode;
+}
+
+void Frame::setInViewSourceMode(bool mode)
+{
+ m_inViewSourceMode = mode;
+}
+
+// Searches from the beginning of the document if nothing is selected.
+bool Frame::findString(const String& target, bool forward, bool caseFlag, bool wrapFlag, bool startInSelection)
+{
+ if (target.isEmpty() || !document())
+ return false;
+
+ if (excludeFromTextSearch())
+ return false;
+
+ // Start from an edge of the selection, if there's a selection that's not in shadow content. Which edge
+ // is used depends on whether we're searching forward or backward, and whether startInSelection is set.
+ RefPtr<Range> searchRange(rangeOfContents(document()));
+ Selection selection = this->selection()->selection();
+
+ if (forward)
+ setStart(searchRange.get(), startInSelection ? selection.visibleStart() : selection.visibleEnd());
+ else
+ setEnd(searchRange.get(), startInSelection ? selection.visibleEnd() : selection.visibleStart());
+
+ Node* shadowTreeRoot = selection.shadowTreeRootNode();
+ if (shadowTreeRoot) {
+ ExceptionCode ec = 0;
+ if (forward)
+ searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec);
+ else
+ searchRange->setStart(shadowTreeRoot, 0, ec);
+ }
+
+ RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, forward, caseFlag));
+ // If we started in the selection and the found range exactly matches the existing selection, find again.
+ // Build a selection with the found range to remove collapsed whitespace.
+ // Compare ranges instead of selection objects to ignore the way that the current selection was made.
+ if (startInSelection && *Selection(resultRange.get()).toNormalizedRange() == *selection.toNormalizedRange()) {
+ searchRange = rangeOfContents(document());
+ if (forward)
+ setStart(searchRange.get(), selection.visibleEnd());
+ else
+ setEnd(searchRange.get(), selection.visibleStart());
+
+ if (shadowTreeRoot) {
+ ExceptionCode ec = 0;
+ if (forward)
+ searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec);
+ else
+ searchRange->setStart(shadowTreeRoot, 0, ec);
+ }
+
+ resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
+ }
+
+ ExceptionCode exception = 0;
+
+ // If nothing was found in the shadow tree, search in main content following the shadow tree.
+ if (resultRange->collapsed(exception) && shadowTreeRoot) {
+ searchRange = rangeOfContents(document());
+ if (forward)
+ searchRange->setStartAfter(shadowTreeRoot->shadowParentNode(), exception);
+ else
+ searchRange->setEndBefore(shadowTreeRoot->shadowParentNode(), exception);
+
+ resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
+ }
+
+ if (!editor()->insideVisibleArea(resultRange.get())) {
+ resultRange = editor()->nextVisibleRange(resultRange.get(), target, forward, caseFlag, wrapFlag);
+ if (!resultRange)
+ return false;
+ }
+
+ // If we didn't find anything and we're wrapping, search again in the entire document (this will
+ // redundantly re-search the area already searched in some cases).
+ if (resultRange->collapsed(exception) && wrapFlag) {
+ searchRange = rangeOfContents(document());
+ resultRange = findPlainText(searchRange.get(), target, forward, caseFlag);
+ // We used to return false here if we ended up with the same range that we started with
+ // (e.g., the selection was already the only instance of this text). But we decided that
+ // this should be a success case instead, so we'll just fall through in that case.
+ }
+
+ if (resultRange->collapsed(exception))
+ return false;
+
+ this->selection()->setSelection(Selection(resultRange.get(), DOWNSTREAM));
+ revealSelection();
+ return true;
+}
+
+unsigned Frame::markAllMatchesForText(const String& target, bool caseFlag, unsigned limit)
+{
+ if (target.isEmpty() || !document())
+ return 0;
+
+ RefPtr<Range> searchRange(rangeOfContents(document()));
+
+ ExceptionCode exception = 0;
+ unsigned matchCount = 0;
+ do {
+ RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, true, caseFlag));
+ if (resultRange->collapsed(exception)) {
+ if (!resultRange->startContainer()->isInShadowTree())
+ break;
+
+ searchRange = rangeOfContents(document());
+ searchRange->setStartAfter(resultRange->startContainer()->shadowAncestorNode(), exception);
+ continue;
+ }
+
+ // A non-collapsed result range can in some funky whitespace cases still not
+ // advance the range's start position (4509328). Break to avoid infinite loop.
+ VisiblePosition newStart = endVisiblePosition(resultRange.get(), DOWNSTREAM);
+ if (newStart == startVisiblePosition(searchRange.get(), DOWNSTREAM))
+ break;
+
+ // Only treat the result as a match if it is visible
+ if (editor()->insideVisibleArea(resultRange.get())) {
+ ++matchCount;
+ document()->addMarker(resultRange.get(), DocumentMarker::TextMatch);
+ }
+
+ // Stop looking if we hit the specified limit. A limit of 0 means no limit.
+ if (limit > 0 && matchCount >= limit)
+ break;
+
+ setStart(searchRange.get(), newStart);
+ Node* shadowTreeRoot = searchRange->shadowTreeRootNode();
+ if (searchRange->collapsed(exception) && shadowTreeRoot)
+ searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), exception);
+ } while (true);
+
+ // Do a "fake" paint in order to execute the code that computes the rendered rect for
+ // each text match.
+ Document* doc = document();
+ if (doc && m_view && contentRenderer()) {
+ doc->updateLayout(); // Ensure layout is up to date.
+ IntRect visibleRect = m_view->visibleContentRect();
+ if (!visibleRect.isEmpty()) {
+ GraphicsContext context((PlatformGraphicsContext*)0);
+ context.setPaintingDisabled(true);
+ m_view->paintContents(&context, visibleRect);
+ }
+ }
+
+ return matchCount;
+}
+
+bool Frame::markedTextMatchesAreHighlighted() const
+{
+ return m_highlightTextMatches;
+}
+
+void Frame::setMarkedTextMatchesAreHighlighted(bool flag)
+{
+ if (flag == m_highlightTextMatches || !document())
+ return;
+
+ m_highlightTextMatches = flag;
+ document()->repaintMarkers(DocumentMarker::TextMatch);
+}
+
+FrameTree* Frame::tree() const
+{
+ return &m_treeNode;
+}
+
+void Frame::setDOMWindow(DOMWindow* domWindow)
+{
+ if (m_domWindow) {
+ m_liveFormerWindows.add(m_domWindow.get());
+ m_domWindow->clear();
+ }
+ m_domWindow = domWindow;
+}
+
+DOMWindow* Frame::domWindow() const
+{
+ if (!m_domWindow)
+ m_domWindow = DOMWindow::create(const_cast<Frame*>(this));
+
+ return m_domWindow.get();
+}
+
+void Frame::clearFormerDOMWindow(DOMWindow* window)
+{
+ m_liveFormerWindows.remove(window);
+}
+
+Page* Frame::page() const
+{
+ return m_page;
+}
+
+EventHandler* Frame::eventHandler() const
+{
+ return &m_eventHandler;
+}
+
+void Frame::pageDestroyed()
+{
+ if (Frame* parent = tree()->parent())
+ parent->loader()->checkLoadComplete();
+
+ // FIXME: It's unclear as to why this is called more than once, but it is,
+ // so page() could be NULL.
+ if (page() && page()->focusController()->focusedFrame() == this)
+ page()->focusController()->setFocusedFrame(0);
+
+#if USE(JSC)
+ // TODO(fqian): Unfork this change. It is a temporary workaround
+ // for this merge to pass layout tests. Once the merge is landed
+ // in the trunk, I am going to unfork this change and fix the issue
+ // in the binding code.
+ script()->clearWindowShell();
+#endif
+
+ // This will stop any JS timers
+#if USE(JSC)
+ if (script()->haveWindowShell())
+ script()->windowShell()->disconnectFrame();
+#elif USE(V8)
+ script()->disconnectFrame();
+#endif
+
+ script()->clearScriptObjects();
+ script()->updatePlatformScriptObjects();
+
+ m_page = 0;
+}
+
+void Frame::disconnectOwnerElement()
+{
+ if (m_ownerElement) {
+ if (Document* doc = document())
+ doc->clearAXObjectCache();
+ m_ownerElement->m_contentFrame = 0;
+ if (m_page)
+ m_page->decrementFrameCount();
+ }
+ m_ownerElement = 0;
+}
+
+String Frame::documentTypeString() const
+{
+ if (Document* doc = document()) {
+ if (DocumentType* doctype = doc->doctype())
+ return createMarkup(doctype);
+ }
+
+ return String();
+}
+
+void Frame::focusWindow()
+{
+ if (!page())
+ return;
+
+ // If we're a top level window, bring the window to the front.
+ if (!tree()->parent())
+ page()->chrome()->focus();
+
+ eventHandler()->focusDocumentView();
+}
+
+void Frame::unfocusWindow()
+{
+ if (!page())
+ return;
+
+ // If we're a top level window, deactivate the window.
+ if (!tree()->parent())
+ page()->chrome()->unfocus();
+}
+
+bool Frame::shouldClose()
+{
+ Chrome* chrome = page() ? page()->chrome() : 0;
+ if (!chrome || !chrome->canRunBeforeUnloadConfirmPanel())
+ return true;
+
+ RefPtr<Document> doc = document();
+ if (!doc)
+ return true;
+ HTMLElement* body = doc->body();
+ if (!body)
+ return true;
+
+ loader()->setFiringUnloadEvents(true);
+
+ RefPtr<BeforeUnloadEvent> beforeUnloadEvent = BeforeUnloadEvent::create();
+ beforeUnloadEvent->setTarget(doc);
+ doc->handleWindowEvent(beforeUnloadEvent.get(), false);
+
+ if (!beforeUnloadEvent->defaultPrevented() && doc)
+ doc->defaultEventHandler(beforeUnloadEvent.get());
+
+ loader()->setFiringUnloadEvents(false);
+
+ if (beforeUnloadEvent->result().isNull())
+ return true;
+
+ String text = doc->displayStringModifiedByEncoding(beforeUnloadEvent->result());
+ return chrome->runBeforeUnloadConfirmPanel(text, this);
+}
+
+void Frame::scheduleClose()
+{
+ if (!shouldClose())
+ return;
+
+ Chrome* chrome = page() ? page()->chrome() : 0;
+ if (chrome)
+ chrome->closeWindowSoon();
+}
+
+void Frame::respondToChangedSelection(const Selection& oldSelection, bool closeTyping)
+{
+ if (document()) {
+ bool isContinuousSpellCheckingEnabled = editor()->isContinuousSpellCheckingEnabled();
+ bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled && editor()->isGrammarCheckingEnabled();
+ if (isContinuousSpellCheckingEnabled) {
+ Selection newAdjacentWords;
+ Selection newSelectedSentence;
+ if (selection()->selection().isContentEditable()) {
+ VisiblePosition newStart(selection()->selection().visibleStart());
+ newAdjacentWords = Selection(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
+ if (isContinuousGrammarCheckingEnabled)
+ newSelectedSentence = Selection(startOfSentence(newStart), endOfSentence(newStart));
+ }
+
+ // When typing we check spelling elsewhere, so don't redo it here.
+ // If this is a change in selection resulting from a delete operation,
+ // oldSelection may no longer be in the document.
+ if (closeTyping && oldSelection.isContentEditable() && oldSelection.start().node() && oldSelection.start().node()->inDocument()) {
+ VisiblePosition oldStart(oldSelection.visibleStart());
+ Selection oldAdjacentWords = Selection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
+ if (oldAdjacentWords != newAdjacentWords) {
+ editor()->markMisspellings(oldAdjacentWords);
+ if (isContinuousGrammarCheckingEnabled) {
+ Selection oldSelectedSentence = Selection(startOfSentence(oldStart), endOfSentence(oldStart));
+ if (oldSelectedSentence != newSelectedSentence)
+ editor()->markBadGrammar(oldSelectedSentence);
+ }
+ }
+ }
+
+ // This only erases markers that are in the first unit (word or sentence) of the selection.
+ // Perhaps peculiar, but it matches AppKit.
+ if (RefPtr<Range> wordRange = newAdjacentWords.toNormalizedRange())
+ document()->removeMarkers(wordRange.get(), DocumentMarker::Spelling);
+ if (RefPtr<Range> sentenceRange = newSelectedSentence.toNormalizedRange())
+ document()->removeMarkers(sentenceRange.get(), DocumentMarker::Grammar);
+ }
+
+ // When continuous spell checking is off, existing markers disappear after the selection changes.
+ if (!isContinuousSpellCheckingEnabled)
+ document()->removeMarkers(DocumentMarker::Spelling);
+ if (!isContinuousGrammarCheckingEnabled)
+ document()->removeMarkers(DocumentMarker::Grammar);
+ }
+
+ editor()->respondToChangedSelection(oldSelection);
+}
+
+VisiblePosition Frame::visiblePositionForPoint(const IntPoint& framePoint)
+{
+ HitTestResult result = eventHandler()->hitTestResultAtPoint(framePoint, true);
+ Node* node = result.innerNode();
+ if (!node)
+ return VisiblePosition();
+ RenderObject* renderer = node->renderer();
+ if (!renderer)
+ return VisiblePosition();
+ VisiblePosition visiblePos = renderer->positionForCoordinates(result.localPoint().x(), result.localPoint().y());
+ if (visiblePos.isNull())
+ visiblePos = VisiblePosition(Position(node, 0));
+ return visiblePos;
+}
+
+Document* Frame::documentAtPoint(const IntPoint& point)
+{
+ if (!view())
+ return 0;
+
+ IntPoint pt = view()->windowToContents(point);
+ HitTestResult result = HitTestResult(pt);
+
+ if (contentRenderer())
+ result = eventHandler()->hitTestResultAtPoint(pt, false);
+ return result.innerNode() ? result.innerNode()->document() : 0;
+}
+
+void Frame::createView(const IntSize& viewportSize,
+ const Color& backgroundColor, bool transparent,
+ const IntSize& fixedLayoutSize, bool useFixedLayout,
+ ScrollbarMode horizontalScrollbarMode, ScrollbarMode verticalScrollbarMode)
+{
+ ASSERT(this);
+ ASSERT(m_page);
+
+ bool isMainFrame = this == m_page->mainFrame();
+
+ if (isMainFrame && view())
+ view()->setParentVisible(false);
+
+ setView(0);
+
+ FrameView* frameView;
+ if (isMainFrame) {
+ frameView = new FrameView(this, viewportSize);
+ frameView->setFixedLayoutSize(fixedLayoutSize);
+ frameView->setUseFixedLayout(useFixedLayout);
+ } else
+ frameView = new FrameView(this);
+
+ frameView->setScrollbarModes(horizontalScrollbarMode, verticalScrollbarMode);
+ frameView->updateDefaultScrollbarState();
+
+ setView(frameView);
+ // FrameViews are created with a ref count of 1. Release this ref since we've assigned it to frame.
+ frameView->deref();
+
+ if (backgroundColor.isValid())
+ frameView->updateBackgroundRecursively(backgroundColor, transparent);
+
+ if (isMainFrame)
+ frameView->setParentVisible(true);
+
+ if (ownerRenderer())
+ ownerRenderer()->setWidget(frameView);
+
+ if (HTMLFrameOwnerElement* owner = ownerElement())
+ view()->setCanHaveScrollbars(owner->scrollingMode() != ScrollbarAlwaysOff);
+}
+
+} // namespace WebCore
« no previous file with comments | « third_party/WebKit/WebCore/page/EventHandler.cpp ('k') | third_party/WebKit/WebCore/platform/ContextMenu.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698