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