Unconditionally detach in Page::WillDetachFrame()
ElementVisibilityObserverTest was using PageTestBase and would
have been tricky to fix properly; instead just migrate it over
to use FrameTestHelpers::WebViewHelper.
PresentationAvailabilityTest is left as a V8TestingScope for
now but should ultimately be converted as well.
Bug: 700783, 734748
Change-Id: Ic73f187a2a1082891168143e37675a763f80db2c
Reviewed-on: https://chromium-review.googlesource.com/1038142
Reviewed-by: Mounir Lamouri <mlamouri@chromium.org>
Commit-Queue: Daniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#556134}diff --git a/third_party/blink/renderer/core/dom/element_visibility_observer_test.cc b/third_party/blink/renderer/core/dom/element_visibility_observer_test.cc
index 3cf8471..5cc0d1a 100644
--- a/third_party/blink/renderer/core/dom/element_visibility_observer_test.cc
+++ b/third_party/blink/renderer/core/dom/element_visibility_observer_test.cc
@@ -7,56 +7,27 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/dom_implementation.h"
-#include "third_party/blink/renderer/core/frame/remote_frame.h"
+#include "third_party/blink/renderer/core/exported/web_remote_frame_impl.h"
+#include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
+#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
#include "third_party/blink/renderer/core/html/html_div_element.h"
#include "third_party/blink/renderer/core/html/html_document.h"
#include "third_party/blink/renderer/core/loader/empty_clients.h"
-#include "third_party/blink/renderer/core/testing/page_test_base.h"
namespace blink {
namespace {
-// Stub implementation of LocalFrameClient for the purpose of testing. It will
-// alow callers to set the parent/top frames by calling |setParent|. It is used
-// in ElementVisibilityObserverTest in order to mock a RemoteFrame parent of a
-// LocalFrame.
-class DOMStubLocalFrameClient final : public EmptyLocalFrameClient {
- public:
- Frame* Parent() const override { return parent_; }
- Frame* Top() const override { return parent_; }
-
- void SetParent(Frame* frame) { parent_ = frame; }
-
- void Trace(blink::Visitor* visitor) override {
- visitor->Trace(parent_);
- EmptyLocalFrameClient::Trace(visitor);
- }
-
- private:
- WeakMember<Frame> parent_ = nullptr;
-};
-
-class ElementVisibilityObserverTest : public PageTestBase {
+class ElementVisibilityObserverTest : public ::testing::Test {
protected:
- void SetUp() override {
- local_frame_client_ = new DOMStubLocalFrameClient();
- SetupPageWithClients(nullptr, local_frame_client_, nullptr);
- }
-
- void TearDown() override { GetFrame().Detach(FrameDetachType::kRemove); }
-
- DOMStubLocalFrameClient* LocalFrameClient() const {
- return local_frame_client_;
- }
-
- private:
- Persistent<DOMStubLocalFrameClient> local_frame_client_;
+ FrameTestHelpers::WebViewHelper helper_;
};
TEST_F(ElementVisibilityObserverTest, ObserveElementWithoutDocumentFrame) {
+ helper_.Initialize();
+ Document& document = *helper_.LocalMainFrame()->GetFrame()->GetDocument();
HTMLElement* element = HTMLDivElement::Create(
- *DOMImplementation::Create(GetDocument())->createHTMLDocument("test"));
+ *DOMImplementation::Create(document)->createHTMLDocument("test"));
ElementVisibilityObserver* observer = new ElementVisibilityObserver(
element, ElementVisibilityObserver::VisibilityCallback());
observer->Start();
@@ -64,12 +35,14 @@
// It should not crash.
}
-TEST_F(ElementVisibilityObserverTest, ObserveElementInRemoteFrame) {
- Persistent<RemoteFrame> remote_frame =
- RemoteFrame::Create(new EmptyRemoteFrameClient(), GetPage(), nullptr);
- LocalFrameClient()->SetParent(remote_frame);
+TEST_F(ElementVisibilityObserverTest, ObserveElementWithRemoteFrameParent) {
+ helper_.InitializeRemote();
- Persistent<HTMLElement> element = HTMLDivElement::Create(GetDocument());
+ WebLocalFrameImpl* child_frame =
+ FrameTestHelpers::CreateLocalChild(*helper_.RemoteMainFrame());
+ Document& document = *child_frame->GetFrame()->GetDocument();
+
+ Persistent<HTMLElement> element = HTMLDivElement::Create(document);
ElementVisibilityObserver* observer =
new ElementVisibilityObserver(element, WTF::BindRepeating([](bool) {}));
observer->Start();
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index cb6f7e392..71d1aaa 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -330,8 +330,14 @@
}
void LocalFrame::Detach(FrameDetachType type) {
- // Note that detach() can be re-entered, so it's not possible to
- // DCHECK(isAttached()) here.
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ // BEGIN RE-ENTRANCY SAFE BLOCK
+ // Starting here, the code must be safe against re-entrancy. Dispatching
+ // events, et cetera can run Javascript, which can reenter Detach().
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ // Detach() can be re-entered, this can't simply DCHECK(IsAttached()).
+ DCHECK_NE(lifecycle_.GetState(), FrameLifecycle::kDetached);
lifecycle_.AdvanceTo(FrameLifecycle::kDetaching);
if (IsLocalRoot()) {
@@ -381,6 +387,14 @@
if (!Client())
return;
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ // END RE-ENTRANCY SAFE BLOCK
+ // Past this point, no script should be executed. If this method was
+ // re-entered, then check for a non-null Client() above should have already
+ // returned.
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ DCHECK_NE(lifecycle_.GetState(), FrameLifecycle::kDetached);
+
// TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout crashes.
CHECK(!view_->IsAttached());
Client()->WillBeDetached();
diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc
index e87c1403..dc89b93 100644
--- a/third_party/blink/renderer/core/page/page.cc
+++ b/third_party/blink/renderer/core/page/page.cc
@@ -743,10 +743,7 @@
void Page::WillBeDestroyed() {
Frame* main_frame = main_frame_;
- // TODO(sashab): Remove this check, the call to detach() here should always
- // work.
- if (main_frame->IsAttached())
- main_frame->Detach(FrameDetachType::kRemove);
+ main_frame->Detach(FrameDetachType::kRemove);
DCHECK(AllPages().Contains(this));
AllPages().erase(this);
diff --git a/third_party/blink/renderer/modules/presentation/presentation_availability_test.cc b/third_party/blink/renderer/modules/presentation/presentation_availability_test.cc
index cc4a1c9..74c0702 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_availability_test.cc
+++ b/third_party/blink/renderer/modules/presentation/presentation_availability_test.cc
@@ -22,22 +22,27 @@
namespace {
TEST(PresentationAvailabilityTest, NoPageVisibilityChangeAfterDetach) {
- V8TestingScope scope;
- WTF::Vector<KURL> urls;
- urls.push_back(URLTestHelpers::ToKURL("https://example.com"));
- urls.push_back(URLTestHelpers::ToKURL("https://another.com"));
+ Page* page = nullptr;
+ {
+ V8TestingScope scope;
+ WTF::Vector<KURL> urls;
+ urls.push_back(URLTestHelpers::ToKURL("https://example.com"));
+ urls.push_back(URLTestHelpers::ToKURL("https://another.com"));
- Persistent<PresentationAvailabilityProperty> resolver =
- new PresentationAvailabilityProperty(
- scope.GetExecutionContext(), nullptr,
- PresentationAvailabilityProperty::kReady);
- Persistent<PresentationAvailability> availability =
- PresentationAvailability::Take(resolver, urls, false);
+ Persistent<PresentationAvailabilityProperty> resolver =
+ new PresentationAvailabilityProperty(
+ scope.GetExecutionContext(), nullptr,
+ PresentationAvailabilityProperty::kReady);
+ Persistent<PresentationAvailability> availability =
+ PresentationAvailability::Take(resolver, urls, false);
- // These two calls should not crash.
- scope.GetFrame().Detach(FrameDetachType::kRemove);
- scope.GetPage().SetVisibilityState(mojom::PageVisibilityState::kHidden,
- false);
+ page = &scope.GetPage();
+ }
+ // This should not crash.
+ // TODO(dcheng): Why are we calling functions on Page after it's been closed?
+ // This case doesn't seem like it should be reachable as we should be shutting
+ // down communication from the embedder on context detach.
+ page->SetVisibilityState(mojom::PageVisibilityState::kHidden, false);
}
} // anonymous namespace