Bug 635852. r=smaug a=lsblakk
authorMats Palmgren <matspal@gmail.com>
Tue, 12 Mar 2013 21:18:11 +0100
changeset 132360 a11d11b69ee7f17c026118c6de2e6c6be8359291
parent 132359 41d7a2a0776b05426a47a7d52eda55ca6fca1592
child 132361 36d27922e4b8d8d2cebf74677c8ec27cf0a544f4
push id2323
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 19:47:02 +0000
treeherdermozilla-beta@7712be144d91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, lsblakk
bugs635852
milestone21.0a2
Bug 635852. r=smaug a=lsblakk
content/events/test/test_bug591249.xul
dom/base/nsDOMWindowUtils.cpp
layout/base/nsPresShell.cpp
layout/base/nsPresShell.h
testing/mochitest/tests/SimpleTest/ChromeUtils.js
toolkit/components/prompts/test/test_bug620145.html
toolkit/components/prompts/test/test_bug625187.html
toolkit/content/tests/chrome/window_browser_drop.xul
--- a/content/events/test/test_bug591249.xul
+++ b/content/events/test/test_bug591249.xul
@@ -24,19 +24,20 @@ https://bugzilla.mozilla.org/show_bug.cg
 SimpleTest.waitForExplicitFinish();
 
 function completeTest(aBox) {
   ok(window.getComputedStyle(aBox).backgroundColor == "rgb(255, 0, 0)", "The -moz-drag-over style should be removed.");
   SimpleTest.finish();
 }
 
 function fireEvent(target, event) {
+  var win = target.ownerDocument.defaultView;
   var utils =
-    window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
-           getInterface(Components.interfaces.nsIDOMWindowUtils);
+    win.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+        getInterface(Components.interfaces.nsIDOMWindowUtils);
   utils.dispatchDOMEventViaPresShell(target, event, true);
 }
 
 function RunTest() {
   var image = document.getElementById("image");
   var iframe = document.getElementById("iframe");
   var iBox = iframe.contentDocument.getElementById("drop-target");
   var insideBoxX = iBox.offsetWidth + 10;
@@ -55,17 +56,17 @@ function RunTest() {
   window.addEventListener("dragstart", trapDrag, true);
   synthesizeMouse(image, 2, 2, { type: "mousedown" });
   synthesizeMouse(image, 11, 11, { type: "mousemove" });
   synthesizeMouse(image, 20, 20, { type: "mousemove" });
   window.removeEventListener("dragstart", trapDrag, true);
   synthesizeMouse(image, 20, 20, { type: "mouseup" });
 
   var event = document.createEvent("DragEvents");
-  event.initDragEvent("dragover", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, iBox, dataTransfer);
+  event.initDragEvent("dragover", true, true, iBox.ownerDocument.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, iBox, dataTransfer);
   fireEvent(iBox, event);
   synthesizeMouse(image, 3, 3, { type: "mousedown" });
   synthesizeMouse(image, 23, 23, { type: "mousemove" });
   synthesizeMouse(iBox, insideBoxX, insideBoxY, { type: "mousemove" });
   ok(window.getComputedStyle(iBox).backgroundColor == "rgb(255, 255, 0)", "The -moz-drag-over style should be applied.");
   synthesizeMouse(iBox, insideBoxX, insideBoxY, { type: "mouseup" });
   window.setTimeout(function () { completeTest(iBox); }, 40);
 }
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1630,30 +1630,33 @@ nsDOMWindowUtils::DispatchDOMEventViaPre
                                                nsIDOMEvent* aEvent,
                                                bool aTrusted,
                                                bool* aRetVal)
 {
   if (!nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
-  nsPresContext* presContext = GetPresContext();
-  NS_ENSURE_STATE(presContext);
-  nsCOMPtr<nsIPresShell> shell = presContext->GetPresShell();
-  NS_ENSURE_STATE(shell);
   NS_ENSURE_STATE(aEvent);
   aEvent->SetTrusted(aTrusted);
   nsEvent* internalEvent = aEvent->GetInternalNSEvent();
   NS_ENSURE_STATE(internalEvent);
   nsCOMPtr<nsIContent> content = do_QueryInterface(aTarget);
   NS_ENSURE_STATE(content);
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
+  if (content->OwnerDoc()->GetWindow() != window) {
+    return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
+  }
+  nsCOMPtr<nsIDocument> targetDoc = content->GetCurrentDoc();
+  NS_ENSURE_STATE(targetDoc);
+  nsRefPtr<nsIPresShell> targetShell = targetDoc->GetShell();
+  NS_ENSURE_STATE(targetShell);
 
   nsEventStatus status = nsEventStatus_eIgnore;
-  shell->HandleEventWithTarget(internalEvent, nullptr, content,
-                               &status);
+  targetShell->HandleEventWithTarget(internalEvent, nullptr, content, &status);
   *aRetVal = (status != nsEventStatus_eConsumeNoDefault);
   return NS_OK;
 }
 
 static void
 InitEvent(nsGUIEvent &aEvent, nsIntPoint *aPt = nullptr)
 {
   if (aPt) {
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -5454,54 +5454,65 @@ nsIPresShell::SetCapturingContent(nsICon
     // CAPTURE_POINTERLOCK is the same as CAPTURE_RETARGETTOELEMENT & CAPTURE_IGNOREALLOWED
     gCaptureInfo.mRetargetToElement = ((aFlags & CAPTURE_RETARGETTOELEMENT) != 0) ||
                                       ((aFlags & CAPTURE_POINTERLOCK) != 0);
     gCaptureInfo.mPreventDrag = (aFlags & CAPTURE_PREVENTDRAG) != 0;
     gCaptureInfo.mPointerLock = (aFlags & CAPTURE_POINTERLOCK) != 0;
   }
 }
 
+nsIContent*
+PresShell::GetCurrentEventContent()
+{
+  if (mCurrentEventContent &&
+      mCurrentEventContent->GetCurrentDoc() != mDocument) {
+    mCurrentEventContent = nullptr;
+    mCurrentEventFrame = nullptr;
+  }
+  return mCurrentEventContent;
+}
+
 nsIFrame*
 PresShell::GetCurrentEventFrame()
 {
   if (MOZ_UNLIKELY(mIsDestroying)) {
     return nullptr;
   }
     
-  if (!mCurrentEventFrame && mCurrentEventContent) {
-    // Make sure the content still has a document reference. If not,
-    // then we assume it is no longer in the content tree and the
-    // frame shouldn't get an event, nor should we even assume its
-    // safe to try and find the frame.
-    if (mCurrentEventContent->GetDocument()) {
-      mCurrentEventFrame = mCurrentEventContent->GetPrimaryFrame();
-    }
-  }
-
+  // GetCurrentEventContent() makes sure the content is still in the
+  // same document that this pres shell belongs to. If not, then the
+  // frame shouldn't get an event, nor should we even assume its safe
+  // to try and find the frame.
+  nsIContent* content = GetCurrentEventContent();
+  if (!mCurrentEventFrame && content) {
+    mCurrentEventFrame = content->GetPrimaryFrame();
+    MOZ_ASSERT(!mCurrentEventFrame ||
+               mCurrentEventFrame->PresContext()->GetPresShell() == this);
+  }
   return mCurrentEventFrame;
 }
 
 nsIFrame*
 PresShell::GetEventTargetFrame()
 {
   return GetCurrentEventFrame();
 }
 
 already_AddRefed<nsIContent>
 PresShell::GetEventTargetContent(nsEvent* aEvent)
 {
-  nsIContent* content = nullptr;
-
-  if (mCurrentEventContent) {
-    content = mCurrentEventContent;
-    NS_IF_ADDREF(content);
+  nsIContent* content = GetCurrentEventContent();
+  if (content) {
+    NS_ADDREF(content);
   } else {
     nsIFrame* currentEventFrame = GetCurrentEventFrame();
     if (currentEventFrame) {
       currentEventFrame->GetContentForEvent(aEvent, &content);
+      NS_ASSERTION(!content || content->GetCurrentDoc() == mDocument,
+                   "handing out content from a different doc");
     } else {
       content = nullptr;
     }
   }
   return content;
 }
 
 void
@@ -5521,16 +5532,23 @@ PresShell::PopCurrentEventInfo()
   mCurrentEventFrame = nullptr;
   mCurrentEventContent = nullptr;
 
   if (0 != mCurrentEventFrameStack.Length()) {
     mCurrentEventFrame = mCurrentEventFrameStack.ElementAt(0);
     mCurrentEventFrameStack.RemoveElementAt(0);
     mCurrentEventContent = mCurrentEventContentStack.ObjectAt(0);
     mCurrentEventContentStack.RemoveObjectAt(0);
+
+    // Don't use it if it has moved to a different document.
+    if (mCurrentEventContent &&
+        mCurrentEventContent->GetCurrentDoc() != mDocument) {
+      mCurrentEventContent = nullptr;
+      mCurrentEventFrame = nullptr;
+    }
   }
 }
 
 bool PresShell::InZombieDocument(nsIContent *aContent)
 {
   // If a content node points to a null document, or the document is not
   // attached to a window, then it is possibly in a zombie document,
   // about to be replaced by a newly loading document.
@@ -6235,17 +6253,17 @@ PresShell::HandleEvent(nsIFrame        *
           rv = static_cast<PresShell*>(shell.get())->
             HandleRetargetedEvent(aEvent, aEventStatus, eventTarget);
         }
         return rv;
       } else {
         mCurrentEventContent = eventTarget;
       }
         
-      if (!mCurrentEventContent || !GetCurrentEventFrame() ||
+      if (!GetCurrentEventContent() || !GetCurrentEventFrame() ||
           InZombieDocument(mCurrentEventContent)) {
         rv = RetargetEventToParent(aEvent, aEventStatus);
         PopCurrentEventInfo();
         return rv;
       }
     } else {
       mCurrentEventFrame = frame;
     }
@@ -6373,16 +6391,26 @@ PresShell::HandlePositionedEvent(nsIFram
   PopCurrentEventInfo();
   return rv;
 }
 
 nsresult
 PresShell::HandleEventWithTarget(nsEvent* aEvent, nsIFrame* aFrame,
                                  nsIContent* aContent, nsEventStatus* aStatus)
 {
+#if DEBUG
+  MOZ_ASSERT(!aFrame || aFrame->PresContext()->GetPresShell() == this,
+             "wrong shell");
+  if (aContent) {
+    nsIDocument* doc = aContent->GetCurrentDoc();
+    NS_ASSERTION(doc, "event for content that isn't in a document");
+    NS_ASSERTION(!doc || doc->GetShell() == this, "wrong shell");
+  }
+#endif
+
   PushCurrentEventInfo(aFrame, aContent);
   nsresult rv = HandleEventInternal(aEvent, aStatus);
   PopCurrentEventInfo();
   return rv;
 }
 
 static bool CanHandleContextMenuEvent(nsMouseEvent* aMouseEvent,
                                         nsIFrame* aFrame)
@@ -6425,17 +6453,17 @@ PresShell::HandleEventInternal(nsEvent* 
     bool isHandlingUserInput = false;
 
     // XXX How about IME events and input events for plugins?
     if (aEvent->mFlags.mIsTrusted) {
       switch (aEvent->message) {
       case NS_KEY_PRESS:
       case NS_KEY_DOWN:
       case NS_KEY_UP: {
-        nsIDocument *doc = mCurrentEventContent ?
+        nsIDocument* doc = GetCurrentEventContent() ?
                            mCurrentEventContent->OwnerDoc() : nullptr;
         nsIDocument* root = nullptr;
         if (static_cast<const nsKeyEvent*>(aEvent)->keyCode == NS_VK_ESCAPE &&
             (root = nsContentUtils::GetRootDocument(doc)) &&
             root->IsFullScreenDoc()) {
           // Prevent default action on ESC key press when exiting
           // DOM full-screen mode. This prevents the browser ESC key
           // handler from stopping all loads in the document, which
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -635,16 +635,17 @@ protected:
   void QueryIsActive();
   nsresult UpdateImageLockingState();
 
 #ifdef ANDROID
   nsIDocument* GetTouchEventTargetDocument();
 #endif
   bool InZombieDocument(nsIContent *aContent);
   already_AddRefed<nsIPresShell> GetParentPresShell();
+  nsIContent* GetCurrentEventContent();
   nsIFrame* GetCurrentEventFrame();
   nsresult RetargetEventToParent(nsGUIEvent* aEvent,
                                  nsEventStatus*  aEventStatus);
   void PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent);
   void PopCurrentEventInfo();
   nsresult HandleEventInternal(nsEvent* aEvent, nsEventStatus *aStatus);
   nsresult HandlePositionedEvent(nsIFrame*      aTargetFrame,
                                  nsGUIEvent*    aEvent,
@@ -732,20 +733,22 @@ protected:
   nsTHashtable<nsPtrHashKey<nsIFrame> > mFramesToDirty;
 
   // Reflow roots that need to be reflowed.
   nsTArray<nsIFrame*>       mDirtyRoots;
 
   nsTArray<nsAutoPtr<nsDelayedEvent> > mDelayedEvents;
   nsRevocableEventPtr<nsRunnableMethod<PresShell> > mResizeEvent;
   nsCOMPtr<nsITimer>        mAsyncResizeEventTimer;
+private:
   nsIFrame*                 mCurrentEventFrame;
   nsCOMPtr<nsIContent>      mCurrentEventContent;
   nsTArray<nsIFrame*>       mCurrentEventFrameStack;
   nsCOMArray<nsIContent>    mCurrentEventContentStack;
+protected:
   nsRevocableEventPtr<nsSynthMouseMoveEvent> mSynthMouseMoveEvent;
   nsCOMPtr<nsIContent>      mLastAnchorScrolledTo;
   nsRefPtr<nsCaret>         mCaret;
   nsRefPtr<nsCaret>         mOriginalCaret;
   nsCallbackEventRequest*   mFirstCallbackEventRequest;
   nsCallbackEventRequest*   mLastCallbackEventRequest;
 
   // This timer controls painting suppression.  Until it fires
--- a/testing/mochitest/tests/SimpleTest/ChromeUtils.js
+++ b/testing/mochitest/tests/SimpleTest/ChromeUtils.js
@@ -220,18 +220,18 @@ function synthesizeDrop(srcElement, dest
   if (!aWindow)
     aWindow = window;
   if (!aDestWindow)
     aDestWindow = aWindow;
 
   var synthesizeMouseAtCenter = (eventUtils || window).synthesizeMouseAtCenter;
   var synthesizeMouse = (eventUtils || window).synthesizeMouse;
 
-  var gWindowUtils  = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
-                             getInterface(Components.interfaces.nsIDOMWindowUtils);
+  var gWindowUtils = aDestWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+                                 getInterface(Components.interfaces.nsIDOMWindowUtils);
   var ds = Components.classes["@mozilla.org/widget/dragservice;1"].
            getService(Components.interfaces.nsIDragService);
 
   var dataTransfer;
   var trapDrag = function(event) {
     dataTransfer = event.dataTransfer;
     for (var i = 0; i < dragData.length; i++) {
       var item = dragData[i];
--- a/toolkit/components/prompts/test/test_bug620145.html
+++ b/toolkit/components/prompts/test/test_bug620145.html
@@ -22,17 +22,16 @@
 </div>
 <button id="button" onmouseup="openAlert()">Button</button>
 
 <script class="testbody" type="text/javascript">
 SimpleTest.waitForExplicitFinish();
 
 var selectionTest = false;
 var testNum = 0;
-var utils = SpecialPowers.DOMWindowUtils;
 
 function hasTabModalPrompts() {
   var prefName = "prompts.tab_modal.enabled";
   var Services = SpecialPowers.Cu
                               .import("resource://gre/modules/Services.jsm")
                               .Services;
   return Services.prefs.getPrefType(prefName) == Services.prefs.PREF_BOOL &&
          Services.prefs.getBoolPref(prefName);
@@ -68,19 +67,21 @@ function runtest()
   dispatchMouseEvent(text, "mousedown");
   dispatchMouseEvent(text, "mouseup");
 
   SimpleTest.finish();
 }
 
 function dispatchMouseEvent(target, type)
 {
+  var win = target.ownerDocument.defaultView;
   e = document.createEvent("MouseEvent");
-  e.initEvent(type, false, false, window, 0, 1, 1, 1, 1,
+  e.initEvent(type, false, false, win, 0, 1, 1, 1, 1,
               false, false, false, false, 0, null);
+  var utils = SpecialPowers.getDOMWindowUtils(win);
   utils.dispatchDOMEventViaPresShell(target, e, true);
   ok(true, type + " sent to " + target.id);
 }
 
 function handleDialog(ui, testNum)
 {
   if (!selectionTest) {
     todo(false, "Selection test is disabled when tab modal prompts are not enabled.");
--- a/toolkit/components/prompts/test/test_bug625187.html
+++ b/toolkit/components/prompts/test/test_bug625187.html
@@ -25,17 +25,16 @@
 
 <pre id="test"></pre>
 
 <script class="testbody" type="text/javascript">
 SimpleTest.waitForExplicitFinish();
 
 var testNum = 0;
 var dialogNum = 0;
-var utils = SpecialPowers.getDOMWindowUtils(window);
 
 function hasTabModalPrompts() {
   var prefName = "prompts.tab_modal.enabled";
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
   Components.utils.import("resource://gre/modules/Services.jsm");
   return Services.prefs.getPrefType(prefName) == Services.prefs.PREF_BOOL &&
          Services.prefs.getBoolPref(prefName);
 }
@@ -92,13 +91,14 @@ function handleDialog(ui)
 }
 
 function dispatchMouseEvent(target, type)
 {
   var win = target.ownerDocument.defaultView;
   var e = document.createEvent("MouseEvent");
   e.initEvent(type, false, false, win, 0, 1, 1, 1, 1,
               false, false, false, false, 0, null);
+  var utils = SpecialPowers.getDOMWindowUtils(win);
   utils.dispatchDOMEventViaPresShell(target, e, true);
 }
 </script>
 </body>
 </html>
--- a/toolkit/content/tests/chrome/window_browser_drop.xul
+++ b/toolkit/content/tests/chrome/window_browser_drop.xul
@@ -78,11 +78,11 @@ function loaded()
   SimpleTest.waitForFocus(runTest);
 }
 
 ]]>
 </script>
 
 <browser id="chromechild" src="about:blank"/>
 <browser id="contentchild" type="content" width="100" height="100"
-         src="data:text/html,&lt;html draggable='true'&gt;&lt;body draggable='true' style='width: 100px; height: 100px;' ondrop='if (window.stopMode) event.stopPropagation(); if (window.cancelMode) event.preventDefault();'&gt;&lt;/body&gt;&lt;/html&gt;"/>
+         src="data:text/html,&lt;html draggable='true'&gt;&lt;body draggable='true' style='width: 100px; height: 100px;' ondragover='event.preventDefault()' ondrop='if (window.stopMode) event.stopPropagation(); if (window.cancelMode) event.preventDefault();'&gt;&lt;/body&gt;&lt;/html&gt;"/>
 
 </window>