Logo Search packages:      
Sourcecode: chromium-browser version File versions  Download package

Frame.cpp

/*
 * 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 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 "CSSMutableStyleDeclaration.h"
#include "CSSProperty.h"
#include "CSSPropertyNames.h"
#include "CachedCSSStyleSheet.h"
#include "Chrome.h"
#include "DOMWindow.h"
#include "DocLoader.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 "RenderPart.h"
#include "RenderTableCell.h"
#include "RenderTextControl.h"
#include "RenderTheme.h"
#include "RenderView.h"
#include "ScriptController.h"
#include "ScriptSourceCode.h"
#include "ScriptValue.h"
#include "Settings.h"
#include "TextIterator.h"
#include "TextResourceDecoder.h"
#include "UserContentURLPattern.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

#if ENABLE(SVG)
#include "SVGDocument.h"
#include "SVGDocumentExtensions.h"
#include "SVGNames.h"
#include "XLinkNames.h"
#endif

#if ENABLE(WML)
#include "WMLNames.h"
#endif

#if ENABLE(MATHML)
#include "MathMLNames.h"
#endif

#if ENABLE(TILED_BACKING_STORE)
#include "TiledBackingStore.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_redirectScheduler(this)
    , m_ownerElement(ownerElement)
    , m_script(this)
    , m_selectionController(this)
    , m_editor(this)
    , m_eventHandler(this)
    , m_animationController(this)
    , m_lifeSupportTimer(this, &Frame::lifeSupportTimerFired)
#if ENABLE(ORIENTATION_EVENTS)
    , m_orientation(0)
#endif
    , m_highlightTextMatches(false)
    , m_inViewSourceMode(false)
    , m_needsReapplyStyles(false)
    , m_isDisconnected(false)
    , m_excludeFromTextSearch(false)
{
    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

#if ENABLE(MATHML)
    MathMLNames::init();
#endif

    XMLNSNames::init();
    XMLNames::init();

    if (!ownerElement) {
        page->setMainFrame(this);
#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.
        ASSERT((!(ownerElement->m_contentFrame)) || (ownerElement->m_contentFrame->ownerElement() != ownerElement));
        ownerElement->m_contentFrame = this;
    }

#ifndef NDEBUG
    frameCounter.increment();
#endif
}

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();

    if (m_view) {
        m_view->hide();
        m_view->clearFrame();
    }

    ASSERT(!m_lifeSupportTimer.isActive());
}

void Frame::init()
{
    m_loader.init();
}

FrameLoader* Frame::loader() const
{
    return &m_loader;
}

RedirectScheduler* Frame::redirectScheduler() const
{
    return &m_redirectScheduler;
}

FrameView* Frame::view() const
{
    return m_view.get();
}

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
}

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;
    selection()->updateSecureKeyboardEntryIfActive();

    if (m_doc && !m_doc->attached())
        m_doc->attach();

    // Update the cached 'document' property, which is now stale.
    m_script.updateDocument();
}

#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;
}

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_selectionController.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()) {
            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)
{
    RenderObject* cellRenderer = cell->renderer();

    if (cellRenderer && cellRenderer->isTableCell()) {
        RenderTableCell* tableCellRenderer = toRenderTableCell(cellRenderer);
        RenderTableCell* cellAboveRenderer = tableCellRenderer->table()->cellAbove(tableCellRenderer);

        if (cellAboveRenderer) {
            HTMLTableCellElement* aboveCell =
                static_cast<HTMLTableCellElement*>(cellAboveRenderer->node());

            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));
}

const VisibleSelection& Frame::mark() const
{
    return m_mark;
}

void Frame::setMark(const VisibleSelection& 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;

    document()->updateStyleIfNeeded();

    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->isTextControl())
        toRenderTextControl(renderer)->selectionChanged(userTriggered);
}

// 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 = toRenderWidget(renderer)->widget();
    return widget && widget->isFrameView();
}

void Frame::setFocusedNodeIfNeeded()
{
    if (selection()->isNone() || !selection()->isFocused())
        return;

    bool caretBrowsing = settings() && settings()->caretBrowsingEnabled();
    if (caretBrowsing) {
        Node* anchor = enclosingAnchorElement(selection()->base());
        if (anchor) {
            page()->focusController()->setFocusedNode(anchor, this);
            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->node();
        }
        document()->setFocusedNode(0);
    }

    if (caretBrowsing)
        page()->focusController()->setFocusedNode(0, this);
}

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);
#else
    UNUSED_PARAM(p);
    UNUSED_PARAM(tx);
    UNUSED_PARAM(ty);
    UNUSED_PARAM(clipRect);
#endif
}

float Frame::zoomFactor() const
{
    return m_zoomFactor;
}

ZoomMode Frame::zoomMode() const
{
    return m_page->settings()->zoomMode();
}

bool Frame::shouldApplyTextZoom() const
{
    return m_zoomFactor != 1.0f && zoomMode() == ZoomTextOnly;
}

bool Frame::shouldApplyPageZoom() const
{
    return m_zoomFactor != 1.0f && zoomMode() == ZoomPage;
}

void Frame::setZoomFactor(float percent, ZoomMode mode)
{
    if (m_zoomFactor == percent && zoomMode() == mode)
        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 (m_doc->isSVGDocument()) {
        if (!static_cast<SVGDocument*>(m_doc.get())->zoomAndPanEnabled())
            return;
        if (m_doc->renderer())
            m_doc->renderer()->setNeedsLayout(true);
    }
#endif

    if (mode == ZoomPage) {
        // 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 = (percent / m_zoomFactor);
        view()->setScrollPosition(IntPoint(scrollPosition.x() * percentDifference, scrollPosition.y() * percentDifference));
    }

    m_zoomFactor = percent;
    m_page->settings()->setZoomMode(mode);

    m_doc->recalcStyle(Node::Force);

    for (Frame* child = tree()->firstChild(); child; child = child->tree()->nextSibling())
        child->setZoomFactor(m_zoomFactor, mode);

    if (m_doc->renderer() && m_doc->renderer()->needsLayout() && view()->didFirstLayout())
        view()->layout();
}

void Frame::setPrinting(bool printing, float minPageWidth, float maxPageWidth, bool adjustViewSize)
{
    m_doc->setPrinting(printing);
    view()->adjustMediaTypeForPrinting(printing);

    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)
{
    ASSERT(m_doc); // Client calls shouldn't be made when the frame is in inconsistent state.
    m_kjsStatusBarText = text;
    if (m_page)
        m_page->chrome()->setStatusbarText(this, m_kjsStatusBarText);
}

void Frame::setJSDefaultStatusBarText(const String& text)
{
    ASSERT(m_doc); // Client calls shouldn't be made when the frame is in inconsistent state.
    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()
{
    // When the frame is not showing web content, it doesn't make sense to apply styles.
    // If we tried, we'd end up doing things with the document, but the document, if one
    // exists, is not currently shown and should be in the page cache.
    if (!m_loader.client()->hasHTMLView())
        return;

    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 function called reapplyStyles.
    // We should probably eventually move it into its own function.
    m_doc->docLoader()->setAutoLoadImages(m_page && m_page->settings()->loadsImagesAutomatically());

    // 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>.
    m_doc->updateStyleSelector();
}

void Frame::injectUserScripts(UserScriptInjectionTime injectionTime)
{
    if (!m_page)
        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->injectionTime() == injectionTime && UserContentURLPattern::matchesPatterns(doc->url(), script->whitelist(), script->blacklist()))
            m_script.evaluateInWorld(ScriptSourceCode(script->source(), script->url()), world);
    }
}

bool Frame::shouldChangeSelection(const VisibleSelection& newSelection) const
{
    return shouldChangeSelection(selection()->selection(), newSelection, newSelection.affinity(), false);
}

bool Frame::shouldChangeSelection(const VisibleSelection& oldSelection, const VisibleSelection& newSelection, EAffinity affinity, bool stillSelecting) const
{
    return editor()->client()->shouldChangeSelectedRange(oldSelection.toNormalizedRange().get(), newSelection.toNormalizedRange().get(),
                                                         affinity, stillSelecting);
}

bool Frame::shouldDeleteSelection(const VisibleSelection& selection) const
{
    return editor()->client()->shouldDeleteRange(selection.toNormalizedRange().get());
}

bool Frame::isContentEditable() const
{
    if (m_editor.clientIsEditable())
        return true;
    return m_doc->inDesignMode();
}

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()) {
        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 (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);
    }

    return value;
}

PassRefPtr<CSSComputedStyleDeclaration> Frame::selectionComputedStyle(Node*& nodeToRemove) const
{
    nodeToRemove = 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()->createElement(spanTag, false);

        styleElement->setAttribute(styleAttr, m_typingStyle->cssText().impl(), ec);
        ASSERT(!ec);

        styleElement->appendChild(document()->createEditingTextNode(""), ec);
        ASSERT(!ec);

        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);

        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
{
    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
{
    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);
    style->setProperty(CSSPropertyWebkitNbspMode, "space", false, ec);
    ASSERT(!ec);
    style->setProperty(CSSPropertyWebkitLineBreak, "after-white-space", false, ec);
    ASSERT(!ec);
}

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;
}

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 toRenderPart(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, SelectionRectRespectTransforms respectTransforms, bool clipToVisibleContent) const
{
    RenderView* root = contentRenderer();
    if (!root)
        return;

    RefPtr<Range> selectedRange = selection()->toNormalizedRange();

    FloatRect visibleContentRect = m_view->visibleContentRect();
    
    // FIMXE: we are appending empty rects to the list for those that fall outside visibleContentRect.
    // We may not want to do that.
    if (respectTransforms) {
        Vector<FloatQuad> quads;
        selectedRange->textQuads(quads, true);

        unsigned size = quads.size();
        for (unsigned i = 0; i < size; ++i) {
            IntRect currRect = quads[i].enclosingBoundingBox();
            if (clipToVisibleContent)
                rects.append(intersection(currRect, visibleContentRect));
            else
                rects.append(currRect);
        }
    } else {
        Vector<IntRect> intRects;
        selectedRange->textRects(intRects, true);

        unsigned size = intRects.size();
        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 ScrollAlignment& alignment, bool revealExtent)
{
    IntRect rect;

    switch (selection()->selectionType()) {
    case VisibleSelection::NoSelection:
        return;
    case VisibleSelection::CaretSelection:
        rect = selection()->absoluteCaretBounds();
        break;
    case VisibleSelection::RangeSelection:
        rect = revealExtent ? VisiblePosition(selection()->extent()).absoluteCaretBounds() : 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);
    }
}

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()->suspendAnimations(document);
            view->frame()->eventHandler()->stopAutoscrollTimer();
        }
    }
}

void Frame::clearTimers()
{
    clearTimers(m_view.get(), document());
}

RenderStyle *Frame::styleForSelectionStart(Node *&nodeToRemove) const
{
    nodeToRemove = 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();

    RefPtr<Element> styleElement = document()->createElement(spanTag, false);

    ExceptionCode ec = 0;
    String styleText = m_typingStyle->cssText() + " display: inline";
    styleElement->setAttribute(styleAttr, styleText.impl(), ec);
    ASSERT(!ec);

    styleElement->appendChild(document()->createEditingTextNode(""), ec);
    ASSERT(!ec);

    node->parentNode()->appendChild(styleElement, ec);
    ASSERT(!ec);

    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();
    bool caretBrowsing = settings() && settings()->caretBrowsingEnabled();
    if (!selection()->isNone() || !(isContentEditable() || caretBrowsing))
        return;

    Node* node = doc->documentElement();
    while (node && !node->hasTagName(bodyTag))
        node = node->traverseNextNode();
    if (node)
        selection()->setSelection(VisibleSelection(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())
        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()));
    VisibleSelection 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 && areRangesEqual(VisibleSelection(resultRange.get()).toNormalizedRange().get(), selection.toNormalizedRange().get())) {
        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(VisibleSelection(resultRange.get(), DOWNSTREAM));
    revealSelection();
    return true;
}

unsigned Frame::markAllMatchesForText(const String& target, bool caseFlag, unsigned limit)
{
    if (target.isEmpty())
        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;
        }

        // 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;

        // Set the new start for the search range to be the end of the previous
        // result range. There is no need to use a VisiblePosition here,
        // since findPlainText will use a TextIterator to go over the visible
        // text nodes. 
        searchRange->setStart(resultRange->endContainer(exception), resultRange->endOffset(exception), exception);

        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 (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)
        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;
}

void Frame::detachFromPage()
{
    m_page = 0;
}

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);

    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();
    bool didTransfer = false;

    // Switch page.
    Page* newPage = newParent ? newParent->page() : 0;
    if (m_page != newPage) {
        if (page()->focusController()->focusedFrame() == this)
            page()->focusController()->setFocusedFrame(0);

        if (m_page)
            m_page->decrementFrameCount();

        m_page = newPage;

        if (newPage)
            newPage->incrementFrameCount();

        didTransfer = true;
    }

    // Update the frame tree.
    Frame* oldParent = tree()->parent();
    if (oldParent != newParent) {
        if (oldParent)
            oldParent->tree()->removeChild(this);
        if (newParent) {
            newParent->tree()->appendChild(this);
            m_ownerElement->setName();
        }
        didTransfer = true;
    }

    // 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();

        // 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();
}

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;

    if (!m_domWindow)
        return true;

    RefPtr<Document> doc = document();
    HTMLElement* body = doc->body();
    if (!body)
        return true;

    RefPtr<BeforeUnloadEvent> beforeUnloadEvent = BeforeUnloadEvent::create();
    m_domWindow->dispatchEvent(beforeUnloadEvent.get(), m_domWindow->document());

    if (!beforeUnloadEvent->defaultPrevented())
        doc->defaultEventHandler(beforeUnloadEvent.get());
    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 VisibleSelection& oldSelection, bool closeTyping)
{
    bool isContinuousSpellCheckingEnabled = editor()->isContinuousSpellCheckingEnabled();
    bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled && editor()->isGrammarCheckingEnabled();
    if (isContinuousSpellCheckingEnabled) {
        VisibleSelection newAdjacentWords;
        VisibleSelection newSelectedSentence;
        bool caretBrowsing = settings() && settings()->caretBrowsingEnabled();
        if (selection()->selection().isContentEditable() || caretBrowsing) {
            VisiblePosition newStart(selection()->selection().visibleStart());
            newAdjacentWords = VisibleSelection(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
            if (isContinuousGrammarCheckingEnabled)
                newSelectedSentence = VisibleSelection(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());
            VisibleSelection oldAdjacentWords = VisibleSelection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
            if (oldAdjacentWords != newAdjacentWords) {
                if (isContinuousGrammarCheckingEnabled) {
                    VisibleSelection oldSelectedSentence = VisibleSelection(startOfSentence(oldStart), endOfSentence(oldStart));
                    editor()->markMisspellingsAndBadGrammar(oldAdjacentWords, oldSelectedSentence != newSelectedSentence, oldSelectedSentence);
                } else
                    editor()->markMisspellingsAndBadGrammar(oldAdjacentWords, false, oldAdjacentWords);
            }
        }

        // 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->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->layoutIfNeededRecursive();
    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());
}
#endif

String Frame::layerTreeAsText() const
{
#if USE(ACCELERATED_COMPOSITING)
    if (!contentRenderer())
        return String();

    GraphicsLayer* rootLayer = contentRenderer()->compositor()->rootPlatformLayer();
    if (!rootLayer)
        return String();
        
    return rootLayer->layerTreeAsText();
#else
    return String();
#endif
}

} // namespace WebCore

Generated by  Doxygen 1.6.0   Back to index