Title: [129910] trunk/Source/WebKit/chromium
Revision
129910
Author
[email protected]
Date
2012-09-28 09:38:08 -0700 (Fri, 28 Sep 2012)

Log Message

[Chromium] Fix the find-in-page implementation for detaching frames.
https://bugs.webkit.org/show_bug.cgi?id=97807

Reviewed by Adam Barth.

Follow-up of 97688. Introduces proper test coverage for the find-in-page
feature in detaching/detached frame situations, fixing a few crashes and
ensuring that a final reply is always sent.

* public/WebNode.h:
* src/WebFrameImpl.cpp:
(WebKit::WebFrameImpl::find):
(WebKit::WebFrameImpl::scopeStringMatches):
(WebKit::WebFrameImpl::flushCurrentScopingEffort):
(WebKit):
(WebKit::WebFrameImpl::finishCurrentScopingEffort):
(WebKit::WebFrameImpl::cancelPendingScopingEffort):
(WebKit::WebFrameImpl::WebFrameImpl):
(WebKit::WebFrameImpl::setWebCoreFrame):
(WebKit::WebFrameImpl::initializeAsMainFrame):
(WebKit::WebFrameImpl::createChildFrame):
(WebKit::WebFrameImpl::shouldScopeMatches):
(WebKit::WebFrameImpl::willDetachPage):
* src/WebFrameImpl.h:
(WebFrameImpl):
* src/WebNode.cpp:
(WebKit::WebNode::remove):
(WebKit):
* tests/WebFrameTest.cpp:
* tests/data/find_in_page.html:

Modified Paths

Diff

Modified: trunk/Source/WebKit/chromium/ChangeLog (129909 => 129910)


--- trunk/Source/WebKit/chromium/ChangeLog	2012-09-28 16:34:07 UTC (rev 129909)
+++ trunk/Source/WebKit/chromium/ChangeLog	2012-09-28 16:38:08 UTC (rev 129910)
@@ -1,3 +1,36 @@
+2012-09-28  Leandro Gracia Gil  <[email protected]>
+
+        [Chromium] Fix the find-in-page implementation for detaching frames.
+        https://bugs.webkit.org/show_bug.cgi?id=97807
+
+        Reviewed by Adam Barth.
+
+        Follow-up of 97688. Introduces proper test coverage for the find-in-page
+        feature in detaching/detached frame situations, fixing a few crashes and
+        ensuring that a final reply is always sent.
+
+        * public/WebNode.h:
+        * src/WebFrameImpl.cpp:
+        (WebKit::WebFrameImpl::find):
+        (WebKit::WebFrameImpl::scopeStringMatches):
+        (WebKit::WebFrameImpl::flushCurrentScopingEffort):
+        (WebKit):
+        (WebKit::WebFrameImpl::finishCurrentScopingEffort):
+        (WebKit::WebFrameImpl::cancelPendingScopingEffort):
+        (WebKit::WebFrameImpl::WebFrameImpl):
+        (WebKit::WebFrameImpl::setWebCoreFrame):
+        (WebKit::WebFrameImpl::initializeAsMainFrame):
+        (WebKit::WebFrameImpl::createChildFrame):
+        (WebKit::WebFrameImpl::shouldScopeMatches):
+        (WebKit::WebFrameImpl::willDetachPage):
+        * src/WebFrameImpl.h:
+        (WebFrameImpl):
+        * src/WebNode.cpp:
+        (WebKit::WebNode::remove):
+        (WebKit):
+        * tests/WebFrameTest.cpp:
+        * tests/data/find_in_page.html:
+
 2012-09-28  Kent Tamura  <[email protected]>
 
         Clean up Localizer-related functions

Modified: trunk/Source/WebKit/chromium/public/WebNode.h (129909 => 129910)


--- trunk/Source/WebKit/chromium/public/WebNode.h	2012-09-28 16:34:07 UTC (rev 129909)
+++ trunk/Source/WebKit/chromium/public/WebNode.h	2012-09-28 16:38:08 UTC (rev 129910)
@@ -113,6 +113,7 @@
     WEBKIT_EXPORT WebNodeList getElementsByTagName(const WebString&) const;
     WEBKIT_EXPORT WebElement rootEditableElement() const;
     WEBKIT_EXPORT bool focused() const;
+    WEBKIT_EXPORT bool remove();
 
     // Returns true if the node has a non-empty bounding box in layout.
     // This does not 100% guarantee the user can see it, but is pretty close.

Modified: trunk/Source/WebKit/chromium/src/WebFrameImpl.cpp (129909 => 129910)


--- trunk/Source/WebKit/chromium/src/WebFrameImpl.cpp	2012-09-28 16:34:07 UTC (rev 129909)
+++ trunk/Source/WebKit/chromium/src/WebFrameImpl.cpp	2012-09-28 16:38:08 UTC (rev 129910)
@@ -525,8 +525,6 @@
 {
 }
 
-// WebFrame -------------------------------------------------------------------
-
 class WebFrameImpl::DeferredScopeStringMatches {
 public:
     DeferredScopeStringMatches(WebFrameImpl* webFrame,
@@ -559,7 +557,6 @@
     bool m_reset;
 };
 
-
 // WebFrame -------------------------------------------------------------------
 
 int WebFrame::instanceCount()
@@ -1618,6 +1615,9 @@
                         bool wrapWithinFrame,
                         WebRect* selectionRect)
 {
+    if (!frame() || !frame()->page())
+        return false;
+
     WebFrameImpl* mainFrameImpl = viewImpl()->mainFrameImpl();
 
     if (!options.findNext)
@@ -1716,13 +1716,15 @@
                                       const WebFindOptions& options,
                                       bool reset)
 {
-    WebFrameImpl* mainFrameImpl = viewImpl()->mainFrameImpl();
-
     if (reset) {
         // This is a brand new search, so we need to reset everything.
         // Scoping is just about to begin.
-        m_scopingComplete = false;
+        m_scopingInProgress = true;
 
+        // Need to keep the current identifier locally in order to finish the
+        // request in case the frame is detached during the process.
+        m_findRequestIdentifier = identifier;
+
         // Clear highlighting for this frame.
         if (frame() && frame()->page() && frame()->editor()->markedTextMatchesAreHighlighted())
             frame()->page()->unmarkAllTextMatches();
@@ -1736,7 +1738,9 @@
 
         m_resumeScopingFromRange = 0;
 
-        mainFrameImpl->m_framesScopingCount++;
+        // The view might be null on detached frames.
+        if (frame() && frame()->page())
+            viewImpl()->mainFrameImpl()->m_framesScopingCount++;
 
         // Now, defer scoping until later to allow find operation to finish quickly.
         scopeStringMatchesSoon(
@@ -1755,6 +1759,7 @@
         return;
     }
 
+    WebFrameImpl* mainFrameImpl = viewImpl()->mainFrameImpl();
     RefPtr<Range> searchRange(rangeOfContents(frame()->document()));
 
     Node* originalEndContainer = searchRange->endContainer();
@@ -1884,21 +1889,30 @@
     finishCurrentScopingEffort(identifier);
 }
 
-void WebFrameImpl::finishCurrentScopingEffort(int identifier)
+void WebFrameImpl::flushCurrentScopingEffort(int identifier)
 {
+    if (!frame() || !frame()->page())
+        return;
+
     WebFrameImpl* mainFrameImpl = viewImpl()->mainFrameImpl();
 
     // This frame has no further scoping left, so it is done. Other frames might,
     // of course, continue to scope matches.
-    m_scopingComplete = true;
     mainFrameImpl->m_framesScopingCount--;
-    m_lastFindRequestCompletedWithNoMatches = !m_lastMatchCount;
 
     // If this is the last frame to finish scoping we need to trigger the final
     // update to be sent.
     if (!mainFrameImpl->m_framesScopingCount)
         mainFrameImpl->increaseMatchCount(0, identifier);
+}
 
+void WebFrameImpl::finishCurrentScopingEffort(int identifier)
+{
+    flushCurrentScopingEffort(identifier);
+
+    m_scopingInProgress = false;
+    m_lastFindRequestCompletedWithNoMatches = !m_lastMatchCount;
+
     // This frame is done, so show any scrollbar tickmarks we haven't drawn yet.
     invalidateArea(InvalidateScrollbar);
 }
@@ -1910,8 +1924,11 @@
 
     m_activeMatchIndexInCurrentFrame = -1;
 
-    if (!m_scopingComplete)
+    // Last request didn't complete.
+    if (m_scopingInProgress)
         m_lastFindRequestCompletedWithNoMatches = false;
+
+    m_scopingInProgress = false;
 }
 
 void WebFrameImpl::increaseMatchCount(int count, int identifier)
@@ -2273,7 +2290,8 @@
 }
 
 WebFrameImpl::WebFrameImpl(WebFrameClient* client)
-    : m_frameLoaderClient(this)
+    : FrameDestructionObserver(0)
+    , m_frameLoaderClient(this)
     , m_client(client)
     , m_frame(0)
     , m_currentActiveMatchFrame(0)
@@ -2283,7 +2301,8 @@
     , m_lastMatchCount(-1)
     , m_totalMatchCount(-1)
     , m_framesScopingCount(-1)
-    , m_scopingComplete(false)
+    , m_findRequestIdentifier(-1)
+    , m_scopingInProgress(false)
     , m_lastFindRequestCompletedWithNoMatches(false)
     , m_nextInvalidateAfter(0)
     , m_findMatchMarkersVersion(0)
@@ -2304,10 +2323,17 @@
     cancelPendingScopingEffort();
 }
 
+void WebFrameImpl::setWebCoreFrame(WebCore::Frame* frame)
+{
+    ASSERT(frame);
+    m_frame = frame;
+    observeFrame(m_frame);
+}
+
 void WebFrameImpl::initializeAsMainFrame(WebCore::Page* page)
 {
     RefPtr<Frame> frame = Frame::create(page, 0, &m_frameLoaderClient);
-    m_frame = frame.get();
+    setWebCoreFrame(frame.get());
 
     // Add reference on behalf of FrameLoader.  See comments in
     // WebFrameLoaderClient::frameLoaderDestroyed for more info.
@@ -2330,7 +2356,7 @@
 
     RefPtr<Frame> childFrame = Frame::create(
         m_frame->page(), ownerElement, &webframe->m_frameLoaderClient);
-    webframe->m_frame = childFrame.get();
+    webframe->setWebCoreFrame(childFrame.get());
 
     childFrame->tree()->setName(request.frameName());
 
@@ -2569,7 +2595,8 @@
 {
     // Don't scope if we can't find a frame or a view.
     // The user may have closed the tab/application, so abort.
-    if (!frame() || !frame()->view())
+    // Also ignore detached frames, as many find operations report to the main frame.
+    if (!frame() || !frame()->view() || !frame()->page())
         return false;
 
     ASSERT(frame()->document() && frame()->view());
@@ -2657,4 +2684,21 @@
         m_frame->document()->loader()->writer()->replaceDocument(scriptResult, ownerDocument.get());
 }
 
+void WebFrameImpl::willDetachPage()
+{
+    if (!frame() || !frame()->page())
+        return;
+
+    // Do not expect string scoping results from any frames that got detached
+    // in the middle of the operation.
+    if (m_scopingInProgress) {
+
+        // There is a possibility that the frame being detached was the only
+        // pending one. We need to make sure final replies can be sent.
+        flushCurrentScopingEffort(m_findRequestIdentifier);
+
+        cancelPendingScopingEffort();
+    }
+}
+
 } // namespace WebKit

Modified: trunk/Source/WebKit/chromium/src/WebFrameImpl.h (129909 => 129910)


--- trunk/Source/WebKit/chromium/src/WebFrameImpl.h	2012-09-28 16:34:07 UTC (rev 129909)
+++ trunk/Source/WebKit/chromium/src/WebFrameImpl.h	2012-09-28 16:38:08 UTC (rev 129910)
@@ -35,6 +35,7 @@
 #include "WebFrame.h"
 
 #include "Frame.h"
+#include "FrameDestructionObserver.h"
 #include "FrameLoaderClientImpl.h"
 #include <wtf/Compiler.h>
 #include <wtf/OwnPtr.h>
@@ -69,7 +70,10 @@
 template <typename T> class WebVector;
 
 // Implementation of WebFrame, note that this is a reference counted object.
-class WebFrameImpl : public WebFrame, public RefCounted<WebFrameImpl> {
+class WebFrameImpl
+    : public WebFrame
+    , public RefCounted<WebFrameImpl>
+    , public WebCore::FrameDestructionObserver {
 public:
     // WebFrame methods:
     virtual WebString uniqueName() const;
@@ -239,6 +243,9 @@
     virtual bool selectionStartHasSpellingMarkerFor(int from, int length) const;
     virtual WebString layerTreeAsText(bool showDebugInfo = false) const;
 
+    // WebCore::FrameDestructionObserver methods.
+    virtual void willDetachPage();
+
     static PassRefPtr<WebFrameImpl> create(WebFrameClient* client);
     virtual ~WebFrameImpl();
 
@@ -330,6 +337,9 @@
     // WebFrameLoaderClient
     void closing();
 
+    // Sets the local WebCore frame and registers destruction observers.
+    void setWebCoreFrame(WebCore::Frame*);
+
     // Notifies the delegate about a new selection rect.
     void reportFindInPageSelection(
         const WebRect& selectionRect, int activeMatchOrdinal, int identifier);
@@ -381,6 +391,11 @@
     // was searched.
     bool shouldScopeMatches(const WTF::String& searchText);
 
+    // Removes the current frame from the global scoping effort and triggers any
+    // updates if appropriate. This method does not mark the scoping operation
+    // as finished.
+    void flushCurrentScopingEffort(int identifier);
+
     // Finishes the current scoping effort and triggers any updates if appropriate.
     void finishCurrentScopingEffort(int identifier);
 
@@ -406,6 +421,7 @@
 
     WebFrameClient* m_client;
 
+    // FIXME: this is redundant as we already have m_frame from FrameDestructionObserver.
     // This is a weak pointer to our corresponding WebCore frame.  A reference to
     // ourselves is held while frame_ is valid.  See our Closing method.
     WebCore::Frame* m_frame;
@@ -437,8 +453,7 @@
 
     // Keeps track of how many matches this frame has found so far, so that we
     // don't loose count between scoping efforts, and is also used (in conjunction
-    // with m_lastSearchString and m_scopingComplete) to figure out if we need to
-    // search the frame again.
+    // with m_lastSearchString) to figure out if we need to search the frame again.
     int m_lastMatchCount;
 
     // This variable keeps a cumulative total of matches found so far for ALL the
@@ -451,10 +466,13 @@
     // It should be -1 for all other frames.
     int m_framesScopingCount;
 
-    // Keeps track of whether the scoping effort was completed (the user may
-    // interrupt it before it completes by submitting a new search).
-    bool m_scopingComplete;
+    // Identifier of the latest find-in-page request. Required to be stored in
+    // the frame in order to reply if required in case the frame is detached.
+    int m_findRequestIdentifier;
 
+    // Keeps track of whether there is an scoping effort ongoing in the frame.
+    bool m_scopingInProgress;
+
     // Keeps track of whether the last find request completed its scoping effort
     // without finding any matches in this frame.
     bool m_lastFindRequestCompletedWithNoMatches;

Modified: trunk/Source/WebKit/chromium/src/WebNode.cpp (129909 => 129910)


--- trunk/Source/WebKit/chromium/src/WebNode.cpp	2012-09-28 16:34:07 UTC (rev 129909)
+++ trunk/Source/WebKit/chromium/src/WebNode.cpp	2012-09-28 16:38:08 UTC (rev 129910)
@@ -223,6 +223,13 @@
     return m_private->focused();
 }
 
+bool WebNode::remove()
+{
+    ExceptionCode exceptionCode = 0;
+    m_private->remove(exceptionCode);
+    return !exceptionCode;
+}
+
 bool WebNode::hasNonEmptyBoundingBox() const
 {
     m_private->document()->updateLayoutIgnorePendingStylesheets();

Modified: trunk/Source/WebKit/chromium/tests/WebFrameTest.cpp (129909 => 129910)


--- trunk/Source/WebKit/chromium/tests/WebFrameTest.cpp	2012-09-28 16:34:07 UTC (rev 129909)
+++ trunk/Source/WebKit/chromium/tests/WebFrameTest.cpp	2012-09-28 16:38:08 UTC (rev 129910)
@@ -900,6 +900,8 @@
     WebFrameImpl* mainFrame = static_cast<WebFrameImpl*>(webView->mainFrame());
     EXPECT_TRUE(mainFrame->find(kFindIdentifier, searchText, options, false, 0));
 
+    mainFrame->resetMatchCount();
+
     for (WebFrame* frame = mainFrame; frame; frame = frame->traverseNext(false))
         frame->scopeStringMatches(kFindIdentifier, searchText, options, true);
 
@@ -990,6 +992,129 @@
     webView->close();
 }
 
+TEST_F(WebFrameTest, FindOnDetachedFrame)
+{
+    registerMockedHttpURLLoad("find_in_page.html");
+    registerMockedHttpURLLoad("find_in_page_frame.html");
+
+    FindUpdateWebFrameClient client;
+    WebView* webView = FrameTestHelpers::createWebViewAndLoad(m_baseURL + "find_in_page.html", true, &client);
+    webView->resize(WebSize(640, 480));
+    webView->layout();
+    webkit_support::RunAllPendingMessages();
+
+    static const char* kFindString = "result";
+    static const int kFindIdentifier = 12345;
+
+    WebFindOptions options;
+    WebString searchText = WebString::fromUTF8(kFindString);
+    WebFrameImpl* mainFrame = static_cast<WebFrameImpl*>(webView->mainFrame());
+    WebFrameImpl* secondFrame = static_cast<WebFrameImpl*>(mainFrame->traverseNext(false));
+    RefPtr<WebCore::Frame> holdSecondFrame = secondFrame->frame();
+
+    // Detach the frame before finding.
+    EXPECT_TRUE(mainFrame->document().getElementById("frame").remove());
+
+    EXPECT_TRUE(mainFrame->find(kFindIdentifier, searchText, options, false, 0));
+    EXPECT_FALSE(secondFrame->find(kFindIdentifier, searchText, options, false, 0));
+
+    webkit_support::RunAllPendingMessages();
+    EXPECT_FALSE(client.findResultsAreReady());
+
+    mainFrame->resetMatchCount();
+
+    for (WebFrame* frame = mainFrame; frame; frame = frame->traverseNext(false))
+        frame->scopeStringMatches(kFindIdentifier, searchText, options, true);
+
+    webkit_support::RunAllPendingMessages();
+    EXPECT_TRUE(client.findResultsAreReady());
+
+    holdSecondFrame.release();
+    webView->close();
+}
+
+TEST_F(WebFrameTest, FindDetachFrameBeforeScopeStrings)
+{
+    registerMockedHttpURLLoad("find_in_page.html");
+    registerMockedHttpURLLoad("find_in_page_frame.html");
+
+    FindUpdateWebFrameClient client;
+    WebView* webView = FrameTestHelpers::createWebViewAndLoad(m_baseURL + "find_in_page.html", true, &client);
+    webView->resize(WebSize(640, 480));
+    webView->layout();
+    webkit_support::RunAllPendingMessages();
+
+    static const char* kFindString = "result";
+    static const int kFindIdentifier = 12345;
+
+    WebFindOptions options;
+    WebString searchText = WebString::fromUTF8(kFindString);
+    WebFrameImpl* mainFrame = static_cast<WebFrameImpl*>(webView->mainFrame());
+    WebFrameImpl* secondFrame = static_cast<WebFrameImpl*>(mainFrame->traverseNext(false));
+    RefPtr<WebCore::Frame> holdSecondFrame = secondFrame->frame();
+
+    for (WebFrame* frame = mainFrame; frame; frame = frame->traverseNext(false))
+        EXPECT_TRUE(frame->find(kFindIdentifier, searchText, options, false, 0));
+
+    webkit_support::RunAllPendingMessages();
+    EXPECT_FALSE(client.findResultsAreReady());
+
+    // Detach the frame between finding and scoping.
+    EXPECT_TRUE(mainFrame->document().getElementById("frame").remove());
+
+    mainFrame->resetMatchCount();
+
+    for (WebFrame* frame = mainFrame; frame; frame = frame->traverseNext(false))
+        frame->scopeStringMatches(kFindIdentifier, searchText, options, true);
+
+    webkit_support::RunAllPendingMessages();
+    EXPECT_TRUE(client.findResultsAreReady());
+
+    holdSecondFrame.release();
+    webView->close();
+}
+
+TEST_F(WebFrameTest, FindDetachFrameWhileScopingStrings)
+{
+    registerMockedHttpURLLoad("find_in_page.html");
+    registerMockedHttpURLLoad("find_in_page_frame.html");
+
+    FindUpdateWebFrameClient client;
+    WebView* webView = FrameTestHelpers::createWebViewAndLoad(m_baseURL + "find_in_page.html", true, &client);
+    webView->resize(WebSize(640, 480));
+    webView->layout();
+    webkit_support::RunAllPendingMessages();
+
+    static const char* kFindString = "result";
+    static const int kFindIdentifier = 12345;
+
+    WebFindOptions options;
+    WebString searchText = WebString::fromUTF8(kFindString);
+    WebFrameImpl* mainFrame = static_cast<WebFrameImpl*>(webView->mainFrame());
+    WebFrameImpl* secondFrame = static_cast<WebFrameImpl*>(mainFrame->traverseNext(false));
+    RefPtr<WebCore::Frame> holdSecondFrame = secondFrame->frame();
+
+    for (WebFrame* frame = mainFrame; frame; frame = frame->traverseNext(false))
+        EXPECT_TRUE(frame->find(kFindIdentifier, searchText, options, false, 0));
+
+    webkit_support::RunAllPendingMessages();
+    EXPECT_FALSE(client.findResultsAreReady());
+
+    mainFrame->resetMatchCount();
+
+    for (WebFrame* frame = mainFrame; frame; frame = frame->traverseNext(false))
+        frame->scopeStringMatches(kFindIdentifier, searchText, options, true);
+
+    // The first scopeStringMatches will have reset the state. Detach before it actually scopes.
+    EXPECT_TRUE(mainFrame->document().getElementById("frame").remove());
+
+    webkit_support::RunAllPendingMessages();
+    EXPECT_TRUE(client.findResultsAreReady());
+
+    holdSecondFrame.release();
+    webView->close();
+}
+
 static WebView* createWebViewForTextSelection(const std::string& url)
 {
     WebView* webView = FrameTestHelpers::createWebViewAndLoad(url, true);

Modified: trunk/Source/WebKit/chromium/tests/data/find_in_page.html (129909 => 129910)


--- trunk/Source/WebKit/chromium/tests/data/find_in_page.html	2012-09-28 16:34:07 UTC (rev 129909)
+++ trunk/Source/WebKit/chromium/tests/data/find_in_page.html	2012-09-28 16:38:08 UTC (rev 129910)
@@ -6,7 +6,7 @@
 <body>
 This a find-in-page match rect test.</br>
 result 00</br>
-<iframe src="" height="300" scrolling="yes"></iframe>
+<iframe src="" id="frame" height="300" scrolling="yes"></iframe>
 </br>
 result 01
 </body>
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to