diff options
Diffstat (limited to 'Source/WebCore/page/Frame.cpp')
-rw-r--r-- | Source/WebCore/page/Frame.cpp | 1002 |
1 files changed, 1002 insertions, 0 deletions
diff --git a/Source/WebCore/page/Frame.cpp b/Source/WebCore/page/Frame.cpp new file mode 100644 index 0000000..11a86c6 --- /dev/null +++ b/Source/WebCore/page/Frame.cpp @@ -0,0 +1,1002 @@ +/* + * 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, 2008, 2009, 2010 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 "CSSComputedStyleDeclaration.h" +#include "CSSMutableStyleDeclaration.h" +#include "CSSProperty.h" +#include "CSSPropertyNames.h" +#include "CachedCSSStyleSheet.h" +#include "Chrome.h" +#include "ChromeClient.h" +#include "DOMWindow.h" +#include "CachedResourceLoader.h" +#include "DocumentType.h" +#include "EditingText.h" +#include "EditorClient.h" +#include "EventNames.h" +#include "FloatQuad.h" +#include "FocusController.h" +#include "FrameLoader.h" +#include "FrameLoaderClient.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "GraphicsLayer.h" +#include "HTMLDocument.h" +#include "HTMLFormControlElement.h" +#include "HTMLFormElement.h" +#include "HTMLFrameElementBase.h" +#include "HTMLNames.h" +#include "HTMLTableCellElement.h" +#include "HitTestResult.h" +#include "Logging.h" +#include "MediaFeatureNames.h" +#include "Navigator.h" +#include "NodeList.h" +#include "Page.h" +#include "PageGroup.h" +#include "RegularExpression.h" +#include "RenderLayer.h" +#include "RenderPart.h" +#include "RenderTableCell.h" +#include "RenderTextControl.h" +#include "RenderTheme.h" +#include "RenderView.h" +#include "ScriptController.h" +#include "ScriptSourceCode.h" +#include "Settings.h" +#include "TextIterator.h" +#include "TextResourceDecoder.h" +#include "UserContentURLPattern.h" +#include "UserTypingGestureIndicator.h" +#include "XMLNSNames.h" +#include "XMLNames.h" +#include "htmlediting.h" +#include "markup.h" +#include "npruntime_impl.h" +#include "visible_units.h" +#include <wtf/RefCountedLeakCounter.h> +#include <wtf/StdLibExtras.h> + +#if USE(ACCELERATED_COMPOSITING) +#include "RenderLayerCompositor.h" +#endif + +#if USE(JSC) +#include "JSDOMWindowShell.h" +#include "runtime_root.h" +#endif + +#include "MathMLNames.h" +#include "SVGNames.h" +#include "XLinkNames.h" + +#if ENABLE(SVG) +#include "SVGDocument.h" +#include "SVGDocumentExtensions.h" +#endif + +#if ENABLE(TILED_BACKING_STORE) +#include "TiledBackingStore.h" +#endif + +#if ENABLE(WML) +#include "WMLNames.h" +#endif + +#if PLATFORM(ANDROID) +#include "WebViewCore.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(); +} + +static inline float parentPageZoomFactor(Frame* frame) +{ + Frame* parent = frame->tree()->parent(); + if (!parent) + return 1; + return parent->pageZoomFactor(); +} + +static inline float parentTextZoomFactor(Frame* frame) +{ + Frame* parent = frame->tree()->parent(); + if (!parent) + return 1; + return parent->textZoomFactor(); +} + +inline Frame::Frame(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* frameLoaderClient) + : m_page(page) + , m_treeNode(this, parentFromOwnerElement(ownerElement)) + , m_loader(this, frameLoaderClient) + , m_navigationScheduler(this) + , m_ownerElement(ownerElement) + , m_script(this) + , m_editor(this) + , m_selectionController(this) + , m_eventHandler(this) + , m_animationController(this) + , m_lifeSupportTimer(this, &Frame::lifeSupportTimerFired) + , m_pageZoomFactor(parentPageZoomFactor(this)) + , m_textZoomFactor(parentTextZoomFactor(this)) + , m_pageScaleFactor(1) +#if ENABLE(ORIENTATION_EVENTS) + , m_orientation(0) +#endif + , m_inViewSourceMode(false) + , m_isDisconnected(false) + , m_excludeFromTextSearch(false) +{ + ASSERT(page); + AtomicString::init(); + HTMLNames::init(); + QualifiedName::init(); + MediaFeatureNames::init(); + SVGNames::init(); + XLinkNames::init(); + MathMLNames::init(); + XMLNSNames::init(); + XMLNames::init(); + +#if ENABLE(WML) + WMLNames::init(); +#endif + + if (!ownerElement) { +#if ENABLE(TILED_BACKING_STORE) + // Top level frame only for now. + setTiledBackingStoreEnabled(page->settings()->tiledBackingStoreEnabled()); +#endif + } else { + page->incrementFrameCount(); + + // Make sure we will not end up with two frames referencing the same owner element. + Frame*& contentFrameSlot = ownerElement->m_contentFrame; + ASSERT(!contentFrameSlot || contentFrameSlot->ownerElement() != ownerElement); + contentFrameSlot = this; + } + +#ifndef NDEBUG + frameCounter.increment(); +#endif +} + +PassRefPtr<Frame> Frame::create(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* client) +{ + RefPtr<Frame> frame = adoptRef(new Frame(page, ownerElement, client)); + if (!ownerElement) + page->setMainFrame(frame); + return frame.release(); +} + +Frame::~Frame() +{ + setView(0); + loader()->cancelAndClear(); + + // FIXME: We should not be doing all this work inside the destructor + + ASSERT(!m_lifeSupportTimer.isActive()); + +#ifndef NDEBUG + frameCounter.decrement(); +#endif + + disconnectOwnerElement(); + + if (m_domWindow) + m_domWindow->disconnectFrame(); + script()->clearWindowShell(); + + HashSet<DOMWindow*>::iterator end = m_liveFormerWindows.end(); + for (HashSet<DOMWindow*>::iterator it = m_liveFormerWindows.begin(); it != end; ++it) + (*it)->disconnectFrame(); + + HashSet<FrameDestructionObserver*>::iterator stop = m_destructionObservers.end(); + for (HashSet<FrameDestructionObserver*>::iterator it = m_destructionObservers.begin(); it != stop; ++it) + (*it)->frameDestroyed(); + + if (m_view) { + m_view->hide(); + m_view->clearFrame(); + } + + ASSERT(!m_lifeSupportTimer.isActive()); +} + +void Frame::addDestructionObserver(FrameDestructionObserver* observer) +{ + m_destructionObservers.add(observer); +} + +void Frame::removeDestructionObserver(FrameDestructionObserver* observer) +{ + m_destructionObservers.remove(observer); +} + +void Frame::setView(PassRefPtr<FrameView> view) +{ + // We the custom scroll bars as early as possible to prevent m_doc->detach() + // from messing with the view such that its scroll bars won't be torn down. + // FIXME: We should revisit this. + if (m_view) + m_view->detachCustomScrollbars(); + + // 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(); + +#if ENABLE(TILED_BACKING_STORE) + if (m_view && tiledBackingStore()) + m_view->setPaintsEntireContents(true); +#endif +} + +void Frame::setDocument(PassRefPtr<Document> newDoc) +{ + ASSERT(!newDoc || newDoc->frame()); + 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; + selection()->updateSecureKeyboardEntryIfActive(); + + if (m_doc && !m_doc->attached()) + m_doc->attach(); + + // Update the cached 'document' property, which is now stale. + m_script.updateDocument(); + + if (m_page) + m_page->updateViewportArguments(); +} + +#if ENABLE(ORIENTATION_EVENTS) +void Frame::sendOrientationChangeEvent(int orientation) +{ + m_orientation = orientation; + if (Document* doc = document()) + doc->dispatchWindowEvent(Event::create(eventNames().orientationchangeEvent, false, false)); +} +#endif // ENABLE(ORIENTATION_EVENTS) + +Settings* Frame::settings() const +{ + return m_page ? m_page->settings() : 0; +} + +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()) { + startsWithWordChar = wordRegExp.match(label.substring(0, 1)) >= 0; + endsWithWordChar = wordRegExp.match(label.substring(label.length() - 1, 1)) >= 0; + } + + if (i) + 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, size_t* resultDistanceFromStartOfCell) +{ + HTMLTableCellElement* aboveCell = cell->cellAbove(); + if (aboveCell) { + // search within the above cell we found for a match + size_t lengthSearched = 0; + 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) { + if (resultDistanceFromStartOfCell) + *resultDistanceFromStartOfCell = lengthSearched; + return nodeString.substring(pos, regExp->matchedLength()); + } + lengthSearched += nodeString.length(); + } + } + } + + // Any reason in practice to search all cells in that are above cell? + if (resultDistanceFromStartOfCell) + *resultDistanceFromStartOfCell = notFound; + return String(); +} + +String Frame::searchForLabelsBeforeElement(const Vector<String>& labels, Element* element, size_t* resultDistance, bool* resultIsInCellAbove) +{ + 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; + + if (resultDistance) + *resultDistance = notFound; + if (resultIsInCellAbove) + *resultIsInCellAbove = 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, resultDistance); + if (!result.isEmpty()) { + if (resultIsInCellAbove) + *resultIsInCellAbove = true; + 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) { + if (resultDistance) + *resultDistance = lengthSearched; + 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) { + String result = searchForLabelsAboveCell(regExp.get(), startingTableCell, resultDistance); + if (!result.isEmpty()) { + if (resultIsInCellAbove) + *resultIsInCellAbove = true; + return result; + } + } + return String(); +} + +static String matchLabelsAgainstString(const Vector<String>& labels, const String& stringToMatch) +{ + if (stringToMatch.isEmpty()) + return String(); + + String mutableStringToMatch = stringToMatch; + + // Make numbers and _'s in field names behave like word boundaries, e.g., "address2" + replace(mutableStringToMatch, RegularExpression("\\d", TextCaseSensitive), " "); + mutableStringToMatch.replace('_', ' '); + + OwnPtr<RegularExpression> regExp(createRegExpForLabels(labels)); + // Use the largest match we can find in the whole string + int pos; + int length; + int bestPos = -1; + int bestLength = -1; + int start = 0; + do { + pos = regExp->match(mutableStringToMatch, start); + if (pos != -1) { + length = regExp->matchedLength(); + if (length >= bestLength) { + bestPos = pos; + bestLength = length; + } + start = pos + 1; + } + } while (pos != -1); + + if (bestPos != -1) + return mutableStringToMatch.substring(bestPos, bestLength); + return String(); +} + +String Frame::matchLabelsAgainstElement(const Vector<String>& labels, Element* element) +{ + // Match against the name element, then against the id element if no match is found for the name element. + // See 7538330 for one popular site that benefits from the id element check. + // FIXME: This code is mirrored in FrameMac.mm. It would be nice to make the Mac code call the platform-agnostic + // code, which would require converting the NSArray of NSStrings to a Vector of Strings somewhere along the way. + String resultFromNameAttribute = matchLabelsAgainstString(labels, element->getAttribute(nameAttr)); + if (!resultFromNameAttribute.isEmpty()) + return resultFromNameAttribute; + + return matchLabelsAgainstString(labels, element->getAttribute(idAttr)); +} + +void Frame::setPrinting(bool printing, const FloatSize& pageSize, float maximumShrinkRatio, AdjustViewSizeOrNot shouldAdjustViewSize) +{ + m_doc->setPrinting(printing); + view()->adjustMediaTypeForPrinting(printing); + + m_doc->styleSelectorChanged(RecalcStyleImmediately); + view()->forceLayoutForPagination(pageSize, maximumShrinkRatio, shouldAdjustViewSize); + + // Subframes of the one we're printing don't lay out to the page size. + for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling()) + child->setPrinting(printing, IntSize(), 0, shouldAdjustViewSize); +} + +void Frame::injectUserScripts(UserScriptInjectionTime injectionTime) +{ + if (!m_page) + return; + + if (loader()->stateMachine()->creatingInitialEmptyDocument()) + return; + + // Walk the hashtable. Inject by world. + const UserScriptMap* userScripts = m_page->group().userScripts(); + if (!userScripts) + return; + UserScriptMap::const_iterator end = userScripts->end(); + for (UserScriptMap::const_iterator it = userScripts->begin(); it != end; ++it) + injectUserScriptsForWorld(it->first.get(), *it->second, injectionTime); +} + +void Frame::injectUserScriptsForWorld(DOMWrapperWorld* world, const UserScriptVector& userScripts, UserScriptInjectionTime injectionTime) +{ + if (userScripts.isEmpty()) + return; + + Document* doc = document(); + if (!doc) + return; + + Vector<ScriptSourceCode> sourceCode; + unsigned count = userScripts.size(); + for (unsigned i = 0; i < count; ++i) { + UserScript* script = userScripts[i].get(); + if (script->injectedFrames() == InjectInTopFrameOnly && ownerElement()) + continue; + + if (script->injectionTime() == injectionTime && UserContentURLPattern::matchesPatterns(doc->url(), script->whitelist(), script->blacklist())) + m_script.evaluateInWorld(ScriptSourceCode(script->source(), script->url()), world); + } +} + +bool Frame::isContentEditable() const +{ + if (m_editor.clientIsEditable()) + return true; + return m_doc->inDesignMode(); +} + +#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; +} + +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); +} + +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 toRenderPart(object); +} + +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()) { + view->frame()->animation()->suspendAnimationsForDocument(document); + view->frame()->eventHandler()->stopAutoscrollTimer(); + } + } +} + +void Frame::clearTimers() +{ + clearTimers(m_view.get(), document()); +} + +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); +} + +void Frame::pageDestroyed() +{ + if (Frame* parent = tree()->parent()) + parent->loader()->checkLoadComplete(); + + if (m_domWindow) + m_domWindow->pageDestroyed(); + + // 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); + + script()->clearWindowShell(); + script()->clearScriptObjects(); + script()->updatePlatformScriptObjects(); + + detachFromPage(); +} + +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; +} + +// The frame is moved in DOM, potentially to another page. +void Frame::transferChildFrameToNewDocument() +{ + ASSERT(m_ownerElement); + Frame* newParent = m_ownerElement->document()->frame(); + ASSERT(newParent); + bool didTransfer = false; + + // Switch page. + Page* newPage = newParent->page(); + Page* oldPage = m_page; + if (m_page != newPage) { + if (m_page) { + if (m_page->focusController()->focusedFrame() == this) + m_page->focusController()->setFocusedFrame(0); + + m_page->decrementFrameCount(); + } + + m_page = newPage; + + if (newPage) + newPage->incrementFrameCount(); + + didTransfer = true; + } + + // Update the frame tree. + didTransfer = newParent->tree()->transferChild(this) || didTransfer; + + // Avoid unnecessary calls to client and frame subtree if the frame ended + // up on the same page and under the same parent frame. + if (didTransfer) { + // Let external clients update themselves. + loader()->client()->didTransferChildFrameToNewDocument(oldPage); + + // Update resource tracking now that frame could be in a different page. + if (oldPage != newPage) + loader()->transferLoadingResourcesFromPage(oldPage); + + // Do the same for all the children. + for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling()) + child->transferChildFrameToNewDocument(); + } +} + +String Frame::documentTypeString() const +{ + if (DocumentType* doctype = document()->doctype()) + return createMarkup(doctype); + + return String(); +} + +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->positionForPoint(result.localPoint()); + 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, bool horizontalLock, + ScrollbarMode verticalScrollbarMode, bool verticalLock) +{ + ASSERT(this); + ASSERT(m_page); + + bool isMainFrame = this == m_page->mainFrame(); + + if (isMainFrame && view()) + view()->setParentVisible(false); + + setView(0); + + RefPtr<FrameView> frameView; + if (isMainFrame) { + frameView = FrameView::create(this, viewportSize); + frameView->setFixedLayoutSize(fixedLayoutSize); + frameView->setUseFixedLayout(useFixedLayout); + } else + frameView = FrameView::create(this); + + frameView->setScrollbarModes(horizontalScrollbarMode, verticalScrollbarMode, horizontalLock, verticalLock); + + setView(frameView); + + 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); +} + +#if ENABLE(TILED_BACKING_STORE) +void Frame::setTiledBackingStoreEnabled(bool enabled) +{ + if (!enabled) { + m_tiledBackingStore.clear(); + return; + } + if (m_tiledBackingStore) + return; + m_tiledBackingStore.set(new TiledBackingStore(this)); + if (m_view) + m_view->setPaintsEntireContents(true); +} + +void Frame::tiledBackingStorePaintBegin() +{ + if (!m_view) + return; + m_view->updateLayoutAndStyleIfNeededRecursive(); + m_view->flushDeferredRepaints(); +} + +void Frame::tiledBackingStorePaint(GraphicsContext* context, const IntRect& rect) +{ + if (!m_view) + return; + m_view->paintContents(context, rect); +} + +void Frame::tiledBackingStorePaintEnd(const Vector<IntRect>& paintedArea) +{ + if (!m_page || !m_view) + return; + unsigned size = paintedArea.size(); + // Request repaint from the system + for (int n = 0; n < size; ++n) + m_page->chrome()->invalidateContentsAndWindow(m_view->contentsToWindow(paintedArea[n]), false); +} + +IntRect Frame::tiledBackingStoreContentsRect() +{ + if (!m_view) + return IntRect(); + return IntRect(IntPoint(), m_view->contentsSize()); +} + +IntRect Frame::tiledBackingStoreVisibleRect() +{ + if (!m_page) + return IntRect(); + return m_page->chrome()->client()->visibleRectForTiledBackingStore(); +} + +Color Frame::tiledBackingStoreBackgroundColor() const +{ + if (!m_view) + return Color(); + return m_view->baseBackgroundColor(); +} +#endif + +String Frame::layerTreeAsText() const +{ +#if USE(ACCELERATED_COMPOSITING) + document()->updateLayout(); + + if (!contentRenderer()) + return String(); + + return contentRenderer()->compositor()->layerTreeAsText(); +#else + return String(); +#endif +} + +void Frame::setPageZoomFactor(float factor) +{ + setPageAndTextZoomFactors(factor, m_textZoomFactor); +} + +void Frame::setTextZoomFactor(float factor) +{ + setPageAndTextZoomFactors(m_pageZoomFactor, factor); +} + +void Frame::setPageAndTextZoomFactors(float pageZoomFactor, float textZoomFactor) +{ + if (m_pageZoomFactor == pageZoomFactor && m_textZoomFactor == textZoomFactor) + return; + + Page* page = this->page(); + if (!page) + return; + + Document* document = this->document(); + if (!document) + return; + +#if ENABLE(SVG) + // Respect SVGs zoomAndPan="disabled" property in standalone SVG documents. + // FIXME: How to handle compound documents + zoomAndPan="disabled"? Needs SVG WG clarification. + if (document->isSVGDocument()) { + if (!static_cast<SVGDocument*>(document)->zoomAndPanEnabled()) + return; + if (document->renderer()) + document->renderer()->setNeedsLayout(true); + } +#endif + + if (m_pageZoomFactor != pageZoomFactor) { + if (FrameView* view = this->view()) { + // Update the scroll position when doing a full page zoom, so the content stays in relatively the same position. + IntPoint scrollPosition = view->scrollPosition(); + float percentDifference = (pageZoomFactor / m_pageZoomFactor); + view->setScrollPosition(IntPoint(scrollPosition.x() * percentDifference, scrollPosition.y() * percentDifference)); + } + } + + m_pageZoomFactor = pageZoomFactor; + m_textZoomFactor = textZoomFactor; + + document->recalcStyle(Node::Force); + + for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling()) + child->setPageAndTextZoomFactors(m_pageZoomFactor, m_textZoomFactor); + + if (FrameView* view = this->view()) { + if (document->renderer() && document->renderer()->needsLayout() && view->didFirstLayout()) + view->layout(); + } +} + +void Frame::scalePage(float scale, const IntPoint& origin) +{ + Document* document = this->document(); + if (!document) + return; + + m_pageScaleFactor = scale; + + if (document->renderer()) + document->renderer()->setNeedsLayout(true); + + document->recalcStyle(Node::Force); + + if (FrameView* view = this->view()) { + if (document->renderer() && document->renderer()->needsLayout() && view->didFirstLayout()) + view->layout(); + view->setScrollPosition(origin); + } +} + +} // namespace WebCore |