Merge inbound to mozilla-central. a=merge
authorNoemi Erli <nerli@mozilla.com>
Sun, 10 Feb 2019 23:38:16 +0200
changeset 458448 6a3edc353ef277fb100bef6c3b1c3e747263cbf3
parent 458447 dd53785791f29cdaf4fb8a3f586c6b182f9ed905 (current diff)
parent 458442 b3bfc680b6c6d11783e119ab07da7e2e47fdec7a (diff)
child 458449 d4a64dd31dcf94e7a275c3ee51b3274bf0fedf47
child 458456 7c2b788ad2a9e03f833be48b888e15664bb2f311
push id111831
push usernerli@mozilla.com
push dateSun, 10 Feb 2019 21:42:43 +0000
treeherdermozilla-inbound@6a3edc353ef2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone67.0a1
first release with
nightly linux32
6a3edc353ef2 / 67.0a1 / 20190210213844 / files
nightly linux64
6a3edc353ef2 / 67.0a1 / 20190210213844 / files
nightly mac
6a3edc353ef2 / 67.0a1 / 20190210213844 / files
nightly win32
6a3edc353ef2 / 67.0a1 / 20190210213844 / files
nightly win64
6a3edc353ef2 / 67.0a1 / 20190210213844 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/devtools/server/actors/replay/control.js
+++ b/devtools/server/actors/replay/control.js
@@ -39,16 +39,19 @@ function ChildProcess(id, recording, rol
 
   this.id = id;
   this.recording = recording;
   this.role = role;
   this.paused = false;
 
   this.lastPausePoint = null;
   this.lastPauseAtRecordingEndpoint = false;
+
+  // The pauseNeeded flag indicates that background replaying children should
+  // not resume execution once the process has paused.
   this.pauseNeeded = false;
 
   // All currently installed breakpoints
   this.breakpoints = [];
 
   // Any debugger requests sent while paused at the current point.
   this.debuggerRequests = [];
 
@@ -216,18 +219,39 @@ ChildProcess.prototype = {
     RecordReplayControl.sendClearBreakpoints(this.id);
   },
 
   sendDebuggerRequest(request) {
     assert(this.paused);
     this.debuggerRequests.push(request);
     return RecordReplayControl.sendDebuggerRequest(this.id, request);
   },
+
+  // When a background child pauses, it does not immediately resume. This will
+  // asynchronously let the role know that it may be able to make progress,
+  // depending on where the active child is and what it is doing.
+  pokeSoon() {
+    if (!this.recording) {
+      Services.tm.dispatchToMainThread(() => {
+        if (this.paused) {
+          this.role.poke();
+        }
+      });
+    }
+  },
 };
 
+function pokeChildren() {
+  for (const child of gChildren) {
+    if (child) {
+      child.pokeSoon();
+    }
+  }
+}
+
 const FlushMs = .5 * 1000;
 const MajorCheckpointMs = 2 * 1000;
 const MinorCheckpointMs = .25 * 1000;
 
 // This section describes the strategy used for managing child processes. When
 // recording, there is a single recording process and two replaying processes.
 // When replaying, there are two replaying processes. The main advantage of
 // using two replaying processes is to provide a smooth experience when
@@ -388,16 +412,21 @@ ChildRoleActive.prototype = {
 
     // When at the endpoint of the recording, immediately resume. We don't
     // want to notify the debugger about this: if the user installed a
     // breakpoint here we will have already gotten a HitExecutionPoint message
     // *without* mRecordingEndpoint set, and we don't want to pause twice at
     // the same point.
     if (msg.recordingEndpoint) {
       resume(true);
+
+      // When resuming at the end of the recording, we will either switch to a
+      // recording child or stay paused at the endpoint. In either case, this
+      // process will stay paused.
+      assert(this.child.paused);
       return;
     }
 
     // Run forward by default if there is no debugger attached, but post a
     // runnable so that callers waiting for the child to pause don't starve.
     if (!gDebugger) {
       Services.tm.dispatchToMainThread(() => this.child.sendResume({ forward: true }));
       return;
@@ -415,26 +444,24 @@ let gLastRecordingCheckpoint;
 // The role taken by replaying children trying to stay close to the active
 // child and save either major or minor checkpoints, depending on whether the
 // active child is paused or rewinding.
 function ChildRoleStandby() {}
 
 ChildRoleStandby.prototype = {
   name: "Standby",
 
-  initialize(child, { startup }) {
+  initialize(child) {
     this.child = child;
-    if (!startup) {
-      this.poke();
-    }
+    this.child.pokeSoon();
   },
 
   hitExecutionPoint(msg) {
     assert(!msg.point.position);
-    this.poke();
+    this.child.pokeSoon();
   },
 
   poke() {
     assert(this.child.paused && !this.child.lastPausePoint.position);
     const currentCheckpoint = this.child.lastCheckpoint();
 
     // Stay paused if we need to while the recording is flushed.
     if (this.child.pauseNeeded) {
@@ -531,38 +558,27 @@ function ChildRoleInert() {}
 ChildRoleInert.prototype = {
   name: "Inert",
 
   initialize() {},
   hitExecutionPoint() {},
   poke() {},
 };
 
-function pokeChildren() {
-  for (const child of gChildren) {
-    if (child && !child.recording && child.paused) {
-      child.pauseNeeded = false;
-      child.role.poke();
-    }
-  }
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // Child Switching
 ////////////////////////////////////////////////////////////////////////////////
 
 // Change the current active child, and select a new role for the old one.
 function switchActiveChild(child, recoverPosition = true) {
   assert(child != gActiveChild);
   assert(gActiveChild.paused);
 
   const oldActiveChild = gActiveChild;
-  child.pauseNeeded = true;
   child.waitUntilPaused();
-  child.pauseNeeded = false;
 
   // Move the installed breakpoints from the old child to the new child.
   assert(child.breakpoints.length == 0);
   for (const pos of oldActiveChild.breakpoints) {
     child.sendAddBreakpoint(pos);
   }
   oldActiveChild.sendClearBreakpoints();
 
@@ -703,27 +719,27 @@ function replayingChildResponsibleForSav
 
 // Synchronously flush the recording to disk.
 function flushRecording() {
   assert(gActiveChild.recording && gActiveChild.paused);
 
   // All replaying children must be paused while the recording is flushed.
   for (const child of gChildren) {
     if (child && !child.recording) {
-      child.pauseNeeded = true;
       child.waitUntilPaused();
     }
   }
 
   gActiveChild.sendFlushRecording();
 
+  // Clear out pauseNeeded state set by any earlier maybeFlushRecording().
   for (const child of gChildren) {
     if (child && !child.recording) {
       child.pauseNeeded = false;
-      child.role.poke();
+      child.pokeSoon();
     }
   }
 
   // After flushing the recording there may be more search results.
   maybeResumeSearch();
 
   gLastRecordingCheckpoint = gActiveChild.lastCheckpoint();
 
@@ -875,19 +891,17 @@ function maybeSendRepaintMessage() {
     if ("width" in rv && "height" in rv) {
       RecordReplayControl.hadRepaint(rv.width, rv.height);
     }
   }
 }
 
 function waitUntilChildHasSavedCheckpoint(child, checkpoint) {
   while (true) {
-    child.pauseNeeded = true;
     child.waitUntilPaused();
-    child.pauseNeeded = false;
     if (child.hasSavedCheckpoint(checkpoint)) {
       return;
     }
     child.role.poke();
   }
 }
 
 function resume(forward) {
@@ -1013,59 +1027,38 @@ const gControl = {
   timeWarp,
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 // Search Operations
 ////////////////////////////////////////////////////////////////////////////////
 
 let gSearchChild;
-let gSearchRestartNeeded;
-
-function maybeRestartSearch() {
-  if (gSearchRestartNeeded && gSearchChild.paused) {
-    if (gSearchChild.lastPausePoint.checkpoint != FirstCheckpointId ||
-        gSearchChild.lastPausePoint.position) {
-      gSearchChild.sendRestoreCheckpoint(FirstCheckpointId);
-      gSearchChild.waitUntilPaused();
-    }
-    gSearchChild.sendClearBreakpoints();
-    gDebugger._forEachSearch(pos => gSearchChild.sendAddBreakpoint(pos));
-    gSearchRestartNeeded = false;
-    gSearchChild.sendResume({ forward: true });
-    return true;
-  }
-  return false;
-}
 
 function ChildRoleSearch() {}
 
 ChildRoleSearch.prototype = {
   name: "Search",
 
   initialize(child, { startup }) {
     this.child = child;
   },
 
   hitExecutionPoint({ point, recordingEndpoint }) {
-    if (maybeRestartSearch()) {
-      return;
-    }
-
     if (point.position) {
       gDebugger._onSearchPause(point);
     }
 
     if (!recordingEndpoint) {
-      this.poke();
+      this.child.pokeSoon();
     }
   },
 
   poke() {
-    if (!gSearchRestartNeeded && !this.child.pauseNeeded) {
+    if (!this.child.pauseNeeded) {
       this.child.sendResume({ forward: true });
     }
   },
 };
 
 function ensureHasSearchChild() {
   if (!gSearchChild) {
     gSearchChild = spawnReplayingChild(new ChildRoleSearch());
@@ -1076,18 +1069,26 @@ function maybeResumeSearch() {
   if (gSearchChild && gSearchChild.paused) {
     gSearchChild.sendResume({ forward: true });
   }
 }
 
 const gSearchControl = {
   reset() {
     ensureHasSearchChild();
-    gSearchRestartNeeded = true;
-    maybeRestartSearch();
+    gSearchChild.waitUntilPaused();
+
+    if (gSearchChild.lastPausePoint.checkpoint != FirstCheckpointId ||
+        gSearchChild.lastPausePoint.position) {
+      gSearchChild.sendRestoreCheckpoint(FirstCheckpointId);
+      gSearchChild.waitUntilPaused();
+    }
+    gSearchChild.sendClearBreakpoints();
+    gDebugger._forEachSearch(pos => gSearchChild.sendAddBreakpoint(pos));
+    gSearchChild.sendResume({ forward: true });
   },
 
   sendRequest(request) { return gSearchChild.sendDebuggerRequest(request); },
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 // Utilities
 ///////////////////////////////////////////////////////////////////////////////
--- a/editor/libeditor/EditorEventListener.cpp
+++ b/editor/libeditor/EditorEventListener.cpp
@@ -202,16 +202,18 @@ void EditorEventListener::Disconnect() {
       mEditorBase->FinalizeSelection();
     }
   }
 
   mEditorBase = nullptr;
 }
 
 void EditorEventListener::UninstallFromEditor() {
+  CleanupDragDropCaret();
+
   nsCOMPtr<EventTarget> piTarget = mEditorBase->GetDOMEventTarget();
   if (!piTarget) {
     return;
   }
 
   EventListenerManager* elmP = piTarget->GetOrCreateListenerManager();
   if (!elmP) {
     return;
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -1043,48 +1043,56 @@ void DrawTargetD2D1::PopLayer() {
   mDC->PopLayer();
 }
 
 already_AddRefed<SourceSurface> DrawTargetD2D1::CreateSourceSurfaceFromData(
     unsigned char *aData, const IntSize &aSize, int32_t aStride,
     SurfaceFormat aFormat) const {
   RefPtr<ID2D1Bitmap1> bitmap;
 
-  HRESULT hr = Factory::GetD2DDeviceContext()->CreateBitmap(
-      D2DIntSize(aSize), aData, aStride,
-      D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE,
-                              D2DPixelFormat(aFormat)),
-      getter_AddRefs(bitmap));
+  RefPtr<ID2D1DeviceContext> dc = Factory::GetD2DDeviceContext();
+  if (!dc) {
+    return nullptr;
+  }
+
+  HRESULT hr =
+      dc->CreateBitmap(D2DIntSize(aSize), aData, aStride,
+                       D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE,
+                                               D2DPixelFormat(aFormat)),
+                       getter_AddRefs(bitmap));
 
   if (FAILED(hr) || !bitmap) {
     gfxCriticalError(
         CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize)))
         << "[D2D1.1] 1CreateBitmap failure " << aSize << " Code: " << hexa(hr)
         << " format " << (int)aFormat;
     return nullptr;
   }
 
-  return MakeAndAddRef<SourceSurfaceD2D1>(
-      bitmap.get(), Factory::GetD2DDeviceContext().get(), aFormat, aSize);
+  return MakeAndAddRef<SourceSurfaceD2D1>(bitmap.get(), dc.get(), aFormat,
+                                          aSize);
 }
 
 already_AddRefed<DrawTarget> DrawTargetD2D1::CreateSimilarDrawTarget(
     const IntSize &aSize, SurfaceFormat aFormat) const {
   RefPtr<DrawTargetD2D1> dt = new DrawTargetD2D1();
 
   if (!dt->Init(aSize, aFormat)) {
     return nullptr;
   }
 
   return dt.forget();
 }
 
 bool DrawTargetD2D1::CanCreateSimilarDrawTarget(const IntSize &aSize,
                                                 SurfaceFormat aFormat) const {
   RefPtr<ID2D1DeviceContext> dc = Factory::GetD2DDeviceContext();
+  if (!dc) {
+    return false;
+  }
   return (dc->GetMaximumBitmapSize() >= UINT32(aSize.width) &&
           dc->GetMaximumBitmapSize() >= UINT32(aSize.height));
 }
 
 already_AddRefed<PathBuilder> DrawTargetD2D1::CreatePathBuilder(
     FillRule aFillRule) const {
   RefPtr<ID2D1PathGeometry> path;
   HRESULT hr = factory()->CreatePathGeometry(getter_AddRefs(path));
@@ -2157,16 +2165,21 @@ already_AddRefed<ID2D1Image> DrawTargetD
 
 already_AddRefed<SourceSurface> DrawTargetD2D1::OptimizeSourceSurface(
     SourceSurface *aSurface) const {
   if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) {
     RefPtr<SourceSurface> surface(aSurface);
     return surface.forget();
   }
 
+  RefPtr<ID2D1DeviceContext> dc = Factory::GetD2DDeviceContext();
+  if (!dc) {
+    return nullptr;
+  }
+
   // Special case captures so we don't resolve them to a data surface.
   if (aSurface->GetType() == SurfaceType::CAPTURE) {
     SourceSurfaceCapture *capture =
         static_cast<SourceSurfaceCapture *>(aSurface);
     RefPtr<SourceSurface> resolved = capture->Resolve(GetBackendType());
     if (!resolved) {
       return nullptr;
     }
@@ -2178,17 +2191,17 @@ already_AddRefed<SourceSurface> DrawTarg
 
   RefPtr<ID2D1Bitmap1> bitmap;
   {
     DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ);
     if (MOZ2D_WARN_IF(!map.IsMapped())) {
       return nullptr;
     }
 
-    HRESULT hr = Factory::GetD2DDeviceContext()->CreateBitmap(
+    HRESULT hr = dc->CreateBitmap(
         D2DIntSize(data->GetSize()), map.GetData(), map.GetStride(),
         D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE,
                                 D2DPixelFormat(data->GetFormat())),
         getter_AddRefs(bitmap));
 
     if (FAILED(hr)) {
       gfxCriticalError(CriticalLog::DefaultOptions(
           Factory::ReasonableSurfaceSize(data->GetSize())))
@@ -2196,18 +2209,17 @@ already_AddRefed<SourceSurface> DrawTarg
           << " Code: " << hexa(hr) << " format " << (int)data->GetFormat();
     }
   }
 
   if (!bitmap) {
     return data.forget();
   }
 
-  return MakeAndAddRef<SourceSurfaceD2D1>(bitmap.get(),
-                                          Factory::GetD2DDeviceContext().get(),
+  return MakeAndAddRef<SourceSurfaceD2D1>(bitmap.get(), dc.get(),
                                           data->GetFormat(), data->GetSize());
 }
 
 void DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC,
                                   ID2D1Geometry *aGeometry,
                                   const D2D1_MATRIX_3X2_F &aTransform,
                                   bool aPixelAligned, bool aForceIgnoreAlpha,
                                   const D2D1_RECT_F &aMaxRect) {
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/bug1496118-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <style>
+    input, input:active { border: none; }
+  </style>
+</head>
+<body>
+<input id="input1">
+<div id=div1 style="height: 100px;">
+  <textarea id="textarea1" style="display: none;"></textarea>
+</div>
+<script>
+SimpleTest.waitForFocus(() => {
+  let input1 = document.getElementById('input1');
+  input1.focus();
+  synthesizeKey("A");
+  document.documentElement.removeAttribute("class");
+});
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/bug1496118.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <style>
+    input, input:active { border: none; }
+  </style>
+</head>
+<body>
+<input id="input1" value="aaaaaaaaaaaaaaaaaaaa">
+<div id=div1 style="height: 100px;">
+  <textarea id="textarea1"></textarea>
+</div>
+<script>
+SimpleTest.waitForFocus(() => {
+  let div1 = document.getElementById('div1');
+  let textarea1 = document.getElementById('textarea1');
+  div1.addEventListener("drop", e => {
+    e.preventDefault();
+
+    textarea1.style = "display: none;";
+    SimpleTest.executeSoon(() => {
+      synthesizeKey("A");
+      document.documentElement.removeAttribute("class");
+    });
+  });
+
+  let input1 = document.getElementById('input1');
+  input1.focus();
+  input1.setSelectionRange(0, input1.value.length);
+
+  synthesizeDrop(input1, textarea1, [[{type: "text/plain", data: "foo"}]]);
+});
+</script>
+</body>
+</html>
--- a/layout/base/tests/mochitest.ini
+++ b/layout/base/tests/mochitest.ini
@@ -322,16 +322,18 @@ support-files =
   bug1423331-2.html
   bug1423331-2-ref.html
   bug1423331-3.html
   bug1423331-4.html
   bug1484094-1.html
   bug1484094-1-ref.html
   bug1484094-2.html
   bug1484094-2-ref.html
+  bug1496118.html
+  bug1496118-ref.html
   bug1510942-1.html
   bug1510942-1-ref.html
   bug1510942-2.html
   bug1510942-2-ref.html
   bug1524266-1.html
   bug1524266-1-ref.html
   bug1524266-2.html
   bug1524266-2-ref.html
--- a/layout/base/tests/test_reftests_with_caret.html
+++ b/layout/base/tests/test_reftests_with_caret.html
@@ -202,16 +202,17 @@ var tests = [
     [ 'bug1415416.html'   , 'bug1415416-ref.html' ] ,
     [ 'bug1423331-1.html' , 'bug1423331-1-ref.html' ] ,
     [ 'bug1423331-2.html' , 'bug1423331-2-ref.html' ] ,
     // FIXME(bug 1434949): These two fail in some platforms.
     // [ 'bug1423331-3.html' , 'bug1423331-1-ref.html' ] ,
     // [ 'bug1423331-4.html' , 'bug1423331-2-ref.html' ] ,
     [ 'bug1484094-1.html' , 'bug1484094-1-ref.html' ] ,
     [ 'bug1484094-2.html' , 'bug1484094-2-ref.html' ] ,
+    [ 'bug1496118.html'   , 'bug1496118-ref.html' ] ,
     [ 'bug1506547-1.html' , 'bug1506547-2.html' ] ,
     [ 'bug1506547-2.html' , 'bug1506547-3.html' ] ,
     [ 'bug1506547-4.html' , 'bug1506547-4-ref.html' ] ,
     [ 'bug1506547-5.html' , 'bug1506547-5-ref.html' ] ,
     [ 'bug1506547-6.html' , 'bug1506547-5-ref.html' ] ,
     [ 'bug1510942-1.html' , 'bug1510942-1-ref.html' ] ,
     [ 'bug1510942-2.html' , 'bug1510942-2-ref.html' ] ,
     [ 'bug1518339-1.html' , 'bug1518339-1-ref.html' ] ,