Bug 1612529. document.open should clear form state from the SHEntry. r=smaug
authorBoris Zbarsky <bzbarsky@mit.edu>
Thu, 06 Feb 2020 17:49:44 +0000
changeset 512779 3e3e0930d279f8f25130926803ba5cbd1badabee
parent 512778 ccd3f1b09bfa94eebf2039ea062318365647db25
child 512780 4d978161c8a3a450b230559e94809369c7663d02
push id37098
push usercsabou@mozilla.com
push dateFri, 07 Feb 2020 03:55:51 +0000
treeherdermozilla-central@7dcafc398876 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1612529
milestone74.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1612529. document.open should clear form state from the SHEntry. r=smaug Otherwise we're using state from the pre-open document for whatever content is being written, which is not likely to be right. Differential Revision: https://phabricator.services.mozilla.com/D61865
docshell/base/nsDocShell.cpp
dom/base/Document.cpp
testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/form-control-state.html
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -917,16 +917,20 @@ void nsDocShell::MaybeHandleSubframeHist
 
 /*
  * Reset state to a new content model within the current document and the
  * document viewer. Called by the document before initiating an out of band
  * document.write().
  */
 NS_IMETHODIMP
 nsDocShell::PrepareForNewContentModel() {
+  // Clear out our form control state, because the state of controls
+  // in the pre-open() document should not affect the state of
+  // controls that are now going to be written.
+  SetLayoutHistoryState(nullptr);
   mEODForCurrentDocument = false;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::FirePageHideNotification(bool aIsUnload) {
   FirePageHideNotificationInternal(aIsUnload, false);
   return NS_OK;
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -8927,16 +8927,21 @@ Document* Document::Open(const Optional<
   mParser = parser;
   parser->Initialize(this, GetDocumentURI(), shell, nullptr);
   nsresult rv = parser->StartExecutor();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aError.Throw(rv);
     return nullptr;
   }
 
+  // Clear out our form control state, because the state of controls
+  // in the pre-open() document should not affect the state of
+  // controls that are now going to be written.
+  mLayoutHistoryState = nullptr;
+
   if (shell) {
     // Prepare the docshell and the document viewer for the impending
     // out-of-band document.write()
     shell->PrepareForNewContentModel();
 
     nsCOMPtr<nsIContentViewer> cv;
     shell->GetContentViewer(getter_AddRefs(cv));
     if (cv) {
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/form-control-state.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Writing out a document with form controls with values</title>
+<link rel="author" href="mailto:bzbarsky@mit.edu"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+function asyncHop(t, arg) {
+  return new Promise(res => t.step_timeout(res.bind(null, arg), 0));
+}
+
+function loadPromise(t, iframe) {
+  var p = new Promise(res =>
+    iframe.addEventListener("load", res.bind(null, iframe), { once: true }));
+  // We need to do one trip through the event loop to make sure we're
+  // not still under the load event firing when we start doing our
+  // document.open bits.
+  return p.then(asyncHop.bind(null, t));
+}
+
+async function createIframe(t) {
+  var i = document.createElement("iframe");
+  t.add_cleanup(() => i.remove());
+  var p = loadPromise(t, i);
+  document.body.appendChild(i);
+  return p;
+}
+
+async function replaceIframe(t, i, text) {
+  var p = loadPromise(t, i);
+  var doc = i.contentDocument;
+  doc.open();
+  doc.write(text);
+  doc.close();
+  return p;
+}
+
+promise_test(async function(t) {
+  var i = await createIframe(t);
+  var str = "<textarea>123</textarea>";
+  await replaceIframe(t, i, str);
+  i.contentDocument.querySelector("textarea").value = "abc";
+  await replaceIframe(t, i, str);
+  assert_equals(i.contentDocument.querySelector("textarea").value, "123");
+}, "textarea state");
+
+promise_test(async function(t) {
+  var i = await createIframe(t);
+  var str = "<input value='123'>";
+  await replaceIframe(t, i, str);
+  i.contentDocument.querySelector("input").value = "abc";
+  await replaceIframe(t, i, str);
+  assert_equals(i.contentDocument.querySelector("input").value, "123");
+}, "input state");
+</script>
+</body>
+</html>