[DL]: Ensure to reject update/commit promises on disconnected elements.
This patch ensures that we reject update/commit when it happens on
a disconnected element.
R=chrishtr@chromium.org
Bug: 882663
Change-Id: Iaee923a4f41714b437c0a2b85bbb175359a2f32c
Reviewed-on: https://chromium-review.googlesource.com/c/1387944
Reviewed-by: Chris Harrelson <chrishtr@chromium.org>
Commit-Queue: Chris Harrelson <chrishtr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#618713}
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.cc b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
index 5e631a67..d58b177 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
@@ -139,8 +139,8 @@
}
ScriptPromise DisplayLockContext::update(ScriptState* script_state) {
- // Reject if we're unlocked.
- if (state_ == kUnlocked)
+ // Reject if we're unlocked or disconnected.
+ if (state_ == kUnlocked || !element_->isConnected())
return GetRejectedPromise(script_state);
// If we have a resolver, then we're at least updating already, just return
@@ -151,16 +151,13 @@
}
update_resolver_ = ScriptPromiseResolver::Create(script_state);
- // We only need to kick off an Update if we're in a locked state, all other
- // states are already updating.
- if (state_ == kLocked)
- StartUpdate();
+ StartUpdateIfNeeded();
return update_resolver_->Promise();
}
ScriptPromise DisplayLockContext::commit(ScriptState* script_state) {
- // Reject if we're unlocked.
- if (state_ == kUnlocked)
+ // Reject if we're unlocked or disonnected.
+ if (state_ == kUnlocked || !element_->isConnected())
return GetRejectedPromise(script_state);
// If we have a resolver, we must be committing already, just return the same
@@ -391,12 +388,19 @@
layout_invalidation_reason::kDisplayLockCommitting);
}
-void DisplayLockContext::StartUpdate() {
- DCHECK_EQ(state_, kLocked);
- state_ = kUpdating;
+void DisplayLockContext::StartUpdateIfNeeded() {
+ // We should not be calling this if we're unlocked.
+ DCHECK_NE(state_, kUnlocked);
+ // Any state other than kLocked means that we are already in the process of
+ // updating/committing, so we can piggy back on that process without kicking
+ // off any new updates.
+ if (state_ != kLocked)
+ return;
+
// We don't need to mark anything dirty since the budget will take care of
// that for us.
update_budget_ = CreateNewBudget();
+ state_ = kUpdating;
ScheduleAnimation();
}
@@ -509,6 +513,8 @@
}
void DisplayLockContext::ScheduleAnimation() {
+ DCHECK(element_->isConnected());
+
// Schedule an animation to perform the lifecycle phases.
element_->GetDocument().GetPage()->Animator().ScheduleVisualUpdate(
element_->GetDocument().GetFrame());
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.h b/third_party/blink/renderer/core/display_lock/display_lock_context.h
index b8956f9..06c767c 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.h
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.h
@@ -149,7 +149,7 @@
// Initiate a commit.
void StartCommit();
// Initiate an update.
- void StartUpdate();
+ void StartUpdateIfNeeded();
// The following functions propagate dirty bits from the locked element up to
// the ancestors in order to be reached. They return true if the element or
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/commit-while-disconnected-expected.html b/third_party/blink/web_tests/display-lock/lock-before-append/commit-while-disconnected-expected.html
new file mode 100644
index 0000000..1333caeb
--- /dev/null
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/commit-while-disconnected-expected.html
@@ -0,0 +1,4 @@
+<!doctype HTML>
+
+<div id="log">PASS</div>
+
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/commit-while-disconnected.html b/third_party/blink/web_tests/display-lock/lock-before-append/commit-while-disconnected.html
new file mode 100644
index 0000000..a5643b93
--- /dev/null
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/commit-while-disconnected.html
@@ -0,0 +1,33 @@
+<!doctype HTML>
+
+<!--
+Runs an acquire(), then commits without connecting the element.
+-->
+
+<div id="log"></div>
+
+<script>
+// TODO(vmpstr): In WPT this needs to be replaced with reftest-wait.
+if (window.testRunner)
+ window.testRunner.waitUntilDone();
+
+function finishTest(status_string) {
+ if (document.getElementById("log").innerHTML === "")
+ document.getElementById("log").innerHTML = status_string;
+ if (window.testRunner)
+ window.testRunner.notifyDone();
+}
+
+function runTest() {
+ let container = document.createElement("div");
+ container.id = "container";
+
+ container.getDisplayLock().acquire({ timeout: Infinity }).then(() => {
+ container.getDisplayLock().commit().then(
+ () => { finishTest("FAIL"); },
+ () => { finishTest("PASS"); });
+ });
+}
+
+window.onload = runTest;
+</script>
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/update-while-disconnected-expected.html b/third_party/blink/web_tests/display-lock/lock-before-append/update-while-disconnected-expected.html
new file mode 100644
index 0000000..1333caeb
--- /dev/null
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/update-while-disconnected-expected.html
@@ -0,0 +1,4 @@
+<!doctype HTML>
+
+<div id="log">PASS</div>
+
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/update-while-disconnected.html b/third_party/blink/web_tests/display-lock/lock-before-append/update-while-disconnected.html
new file mode 100644
index 0000000..9f0e4ed
--- /dev/null
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/update-while-disconnected.html
@@ -0,0 +1,33 @@
+<!doctype HTML>
+
+<!--
+Runs an acquire(), then updates without connecting the element.
+-->
+
+<div id="log"></div>
+
+<script>
+// TODO(vmpstr): In WPT this needs to be replaced with reftest-wait.
+if (window.testRunner)
+ window.testRunner.waitUntilDone();
+
+function finishTest(status_string) {
+ if (document.getElementById("log").innerHTML === "")
+ document.getElementById("log").innerHTML = status_string;
+ if (window.testRunner)
+ window.testRunner.notifyDone();
+}
+
+function runTest() {
+ let container = document.createElement("div");
+ container.id = "container";
+
+ container.getDisplayLock().acquire({ timeout: Infinity }).then(() => {
+ container.getDisplayLock().update().then(
+ () => { finishTest("FAIL"); },
+ () => { finishTest("PASS"); });
+ });
+}
+
+window.onload = runTest;
+</script>