Bug 609866 - Fennec fires contextmenu event in content during panning/tapping [r=mfinkle,wesj]
authorVivien Nicolas <21@vingtetun.org>
Wed, 05 Jan 2011 12:58:32 +0100
changeset 67210 f98f55ee90cff021c7c6077c7dfd979424eacddf
parent 67209 e6bc890dd616def8b9fb1c5a260dd450afef9a12
child 67211 594378bf821b23a3f9e23c8ccab08aa2470f314b
push idunknown
push userunknown
push dateunknown
reviewersmfinkle, wesj
bugs609866
Bug 609866 - Fennec fires contextmenu event in content during panning/tapping [r=mfinkle,wesj]
mobile/chrome/content/browser.js
mobile/chrome/content/content.js
mobile/chrome/tests/browser_tap_content.html
mobile/chrome/tests/browser_tapping.js
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -1522,19 +1522,19 @@ const ContentTouchHandler = {
     document.addEventListener("TapDouble", this, false);
     document.addEventListener("TapLong", this, false);
 
     document.addEventListener("PanBegin", this, false);
     document.addEventListener("PopupChanged", this, false);
     document.addEventListener("CancelTouchSequence", this, false);
 
     // Context menus have the following flow:
-    //   [parent] mousedown -> TapDown -> Browser:MouseDown
-    //   [child]  Browser:MouseDown -> contextmenu -> Browser:ContextMenu
-    //   [parent] Browser:ContextMenu -> ...* -> TapLong
+    //   [parent] mousedown -> TapLong -> Browser:MouseLong
+    //   [child]  Browser:MouseLong -> contextmenu -> Browser:ContextMenu
+    //   [parent] Browser:ContextMenu -> ...*
     //
     // * = Here some time will elapse. Although we get the context menu we need
     //     ASAP, we do not act on the context menu until we receive a LongTap.
     //     This is so we can show the context menu as soon as we know it is
     //     a long tap, without waiting for child process.
     //
     messageManager.addMessageListener("Browser:ContextMenu", this);
 
@@ -1578,31 +1578,37 @@ const ContentTouchHandler = {
             break;
           case "TapSingle":
             this.tapSingle(aEvent.clientX, aEvent.clientY, aEvent.modifiers);
             break;
           case "TapDouble":
             this.tapDouble(aEvent.clientX, aEvent.clientY, aEvent.modifiers);
             break;
           case "TapLong":
-            this.tapLong();
+            this.tapLong(aEvent.clientX, aEvent.clientY);
             break;
         }
       }
     }
   },
 
   receiveMessage: function receiveMessage(aMessage) {
     if (aMessage.json.messageId != this._messageId)
       return;
 
     switch (aMessage.name) {
       case "Browser:ContextMenu":
         // Long tap
-        this._contextMenu = { name: aMessage.name, json: aMessage.json, target: aMessage.target };
+        let contextMenu = { name: aMessage.name, json: aMessage.json, target: aMessage.target };
+        if (ContextHelper.showPopup(contextMenu)) {
+          // Stop all input sequences
+          let event = document.createEvent("Events");
+          event.initEvent("CancelTouchSequence", true, false);
+          document.dispatchEvent(event);
+        }
         break;
 
       case "Browser:Highlight": {
         let rects = aMessage.json.rects.map(function(r) new Rect(r.left, r.top, r.width, r.height));
         TapHighlightHelper.show(rects);
         break;
       }
     }
@@ -1674,26 +1680,18 @@ const ContentTouchHandler = {
     let tab = Browser.selectedTab;
     if (!tab.allowZoom)
       return;
 
     let width = window.innerWidth / Browser.getScaleRatio();
     this._dispatchMouseEvent("Browser:ZoomToPoint", aX, aY, { width: width });
   },
 
-  tapLong: function tapLong() {
-    if (this._contextMenu) {
-      if (ContextHelper.showPopup(this._contextMenu)) {
-        // Stop all input sequences
-        let event = document.createEvent("Events");
-        event.initEvent("CancelTouchSequence", true, false);
-        document.dispatchEvent(event);
-      }
-      this._contextMenu = null;
-    }
+  tapLong: function tapLong(aX, aY) {
+    this._dispatchMouseEvent("Browser:MouseLong", aX, aY);
   },
 
   toString: function toString() {
     return "[ContentTouchHandler] { }";
   }
 };
 
 
--- a/mobile/chrome/content/content.js
+++ b/mobile/chrome/content/content.js
@@ -306,18 +306,18 @@ ProgressController.prototype = {
 
 
 /** Can't think of a good description of this class.  It probably does too much? */
 function Content() {
   this._isZoomedToElement = false;
 
   addMessageListener("Browser:Blur", this);
   addMessageListener("Browser:KeyEvent", this);
-  addMessageListener("Browser:MouseDown", this);
   addMessageListener("Browser:MouseOver", this);
+  addMessageListener("Browser:MouseLong", this);
   addMessageListener("Browser:MouseUp", this);
   addMessageListener("Browser:SaveAs", this);
   addMessageListener("Browser:ZoomToPoint", this);
   addMessageListener("Browser:MozApplicationCache:Fetch", this);
 
   if (Util.isParentProcess())
     addEventListener("DOMActivate", this, true);
 
@@ -419,29 +419,16 @@ Content.prototype = {
             shiftKey: json.modifiers & masks.SHIFT_MASK,
             metaKey: json.modifiers & masks.META_MASK,
             keyCode: json.keyCode,
             charCode: json.charCode
           });
         }
         break;
 
-      case "Browser:MouseDown": {
-        let element = elementFromPoint(x, y);
-        if (!element)
-          return;
-
-        ContextHandler.messageId = json.messageId;
-
-        let event = content.document.createEvent("PopupEvents");
-        event.initEvent("contextmenu", true, true);
-        element.dispatchEvent(event);
-        break;
-      }
-
       case "Browser:MouseOver": {
         let element = elementFromPoint(x, y);
         if (!element)
           return;
 
         // Sending a mousemove force the dispatching of mouseover/mouseout
         this._sendMouseEvent("mousemove", element, x, y);
 
@@ -460,16 +447,29 @@ Content.prototype = {
         if (targetElement) {
           let rect = getOverflowContentBoundingRect(targetElement);
           let highlightRects = [{ left: rect.x, top: rect.y, width: rect.width, height: rect.height }];
           sendAsyncMessage("Browser:Highlight", { rects: highlightRects, messageId: json.messageId });
         }
         break;
       }
 
+      case "Browser:MouseLong": {
+        let element = elementFromPoint(x, y);
+        if (!element)
+          return;
+
+        ContextHandler.messageId = json.messageId;
+
+        let event = content.document.createEvent("PopupEvents");
+        event.initEvent("contextmenu", true, true);
+        element.dispatchEvent(event);
+        break;
+      }
+
       case "Browser:MouseUp": {
         this._formAssistant.focusSync = true;
         let element = elementFromPoint(x, y);
         if (modifiers == Ci.nsIDOMNSEvent.CONTROL_MASK) {
           let uri = Util.getHrefForElement(element);
           if (uri)
             sendAsyncMessage("Browser:OpenURI", { uri: uri,
                                                   referrer: element.ownerDocument.documentURIObject.spec });
--- a/mobile/chrome/tests/browser_tap_content.html
+++ b/mobile/chrome/tests/browser_tap_content.html
@@ -1,16 +1,18 @@
 <html style="border: 1px solid red">
 <title>Browser Tap Page 01</title>
 <body>
   <p style="margin-bottom:100px">Here are some images and links. We use them to test context menu (long taps)</p>
 
   <div style="margin-bottom:20px">
     <div>A plain link</div>
-    <a id="link-single" href="browser_blank_01.html">A blank page - nothing interesting</a>
+    <a id="link-disabled" href="browser_blank_01.html" oncontextmenu="return false;">A blank page - no context menu</a>
+    <br />
+    <a id="link-single" href="browser_blank_01.html" title="nothingInteresting">A blank page - nothing interesting</a>
   </div>
 
   <div style="margin-bottom:20px">
     <div>A plain image</div>
     <img id="img-single" src="data:image/jpeg,%FF%D8%FF%E0%00%10JFIF%00%01%01%00%00%01%00%01%00%00%FF%FE%00%3BCREATOR%3A%20gd-jpeg%20v1.0%20(using%20IJG%20JPEG%20v62)%2C%20quality%20%3D%2090%0A%FF%DB%00C%00%03%02%02%03%02%02%03%03%03%03%04%03%03%04%05%08%05%05%04%04%05%0A%07%07%06%08%0C%0A%0C%0C%0B%0A%0B%0B%0D%0E%12%10%0D%0E%11%0E%0B%0B%10%16%10%11%13%14%15%15%15%0C%0F%17%18%16%14%18%12%14%15%14%FF%DB%00C%01%03%04%04%05%04%05%09%05%05%09%14%0D%0B%0D%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%14%FF%C0%00%11%08%00%24%00%24%03%01%22%00%02%11%01%03%11%01%FF%C4%00%1F%00%00%01%05%01%01%01%01%01%01%00%00%00%00%00%00%00%00%01%02%03%04%05%06%07%08%09%0A%0B%FF%C4%00%B5%10%00%02%01%03%03%02%04%03%05%05%04%04%00%00%01%7D%01%02%03%00%04%11%05%12!1A%06%13Qa%07%22q%142%81%91%A1%08%23B%B1%C1%15R%D1%F0%243br%82%09%0A%16%17%18%19%1A%25%26'()*456789%3ACDEFGHIJSTUVWXYZcdefghijstuvwxyz%83%84%85%86%87%88%89%8A%92%93%94%95%96%97%98%99%9A%A2%A3%A4%A5%A6%A7%A8%A9%AA%B2%B3%B4%B5%B6%B7%B8%B9%BA%C2%C3%C4%C5%C6%C7%C8%C9%CA%D2%D3%D4%D5%D6%D7%D8%D9%DA%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FF%C4%00%1F%01%00%03%01%01%01%01%01%01%01%01%01%00%00%00%00%00%00%01%02%03%04%05%06%07%08%09%0A%0B%FF%C4%00%B5%11%00%02%01%02%04%04%03%04%07%05%04%04%00%01%02w%00%01%02%03%11%04%05!1%06%12AQ%07aq%13%222%81%08%14B%91%A1%B1%C1%09%233R%F0%15br%D1%0A%16%244%E1%25%F1%17%18%19%1A%26'()*56789%3ACDEFGHIJSTUVWXYZcdefghijstuvwxyz%82%83%84%85%86%87%88%89%8A%92%93%94%95%96%97%98%99%9A%A2%A3%A4%A5%A6%A7%A8%A9%AA%B2%B3%B4%B5%B6%B7%B8%B9%BA%C2%C3%C4%C5%C6%C7%C8%C9%CA%D2%D3%D4%D5%D6%D7%D8%D9%DA%E2%E3%E4%E5%E6%E7%E8%E9%EA%F2%F3%F4%F5%F6%F7%F8%F9%FA%FF%DA%00%0C%03%01%00%02%11%03%11%00%3F%00%F8%D7C%F1%5CW%DF%194%FDf%F2%F5%26%8AR%3C%EB%82%C0%8D%BEQBI%1E%82%B8%3F%18%7Cm%F1G%8D4%3D%0FA%7DFX%F4%AD%26%D1--%AC%E0%FD%DCe%23%18V%60%3E%F3%11%D4%9C%FB%60W%23gos96%D6%EE%242%F0%10(%04%E7%AFJ%F4%CF%82%FF%00%0E%2C%EF%ED%A5%D7%B5%E8%5C%D8A!%89a%11%92X%8E%A7%03%9E%BC~u%CDR%AA%A6%A5%2B%EEm%87%C2%FBNXv%EAs%FE%11%97UeYD%97%11%A5%AF%EFCE%2F%20%8E%A7o%F9%F5%CDz%BF%C3%BF%19x%82%EFH%D5%B4W%D6%AF%5E%CE%FA%DEKYl%24%95%DA%22%0Cm%8F%90%F4%25%D5%08%E3%A9%1D%06k%E9_%82%DE%12%F0%07%8B%2FF%9D%1Cztf%E16%B5%B4%F6%C6%19%24_%E2%03p%04%FE%15%DA%FCN%FD%9Ac%F8u%E1%3F%F8O%7C6%B6%D2%D9i%12G%F6%8D8B%04%9ECH%A1%B0%F9%C9%23%3Cw8%EB%9CW%241R%95%EF%1B%1E%BDL%BE%10%8D%F9%93%3F9%E6%07%7D%15%B9%E3%98l%A0%F1v%AA%9Al%82%5B%0F%B49%85%80%C7%C8I%20c%DB8%FC(%AD%13%3C%87%B9%E5%BE%0F%BEXu%FBS2%3C%88K)X%FA%8C%A9%19%E7%D39%3FJ%FB%7F%E0%D7%86t%FF%00%EC(%F4%B9-%BC%EB)%8E%D7%8C%9E%08''%3F%9Dx%DF%80%BFf%1DCM%D2%AF%B5%3Df%16%93X6%EEl%AD%20%C9T%90%00%40%24%03%B9%9B%EE%81%C7%5E%F9%06%BD%F3%E1%95%FC~%1E%B0Qp%85X.%E5c%FC%40%8E%08%AE%5C%7BM%FB%A7%BD%94%C9E%3Eu%A7%F5%FEGu%F0%FB%C1%DAF%97%F1%9F%C3%9A%0E%93%17%92%24%93%CB%91%22F1%C4%AC%09%CEO%1B%B9%CE%075%2F%ED%19%F0%F7Q%F89%FB6%F8%A7Yo%1E%EA%1A%D0%D6o%ED%AC~%CE%D2%E2%19%B3%2F%9B%B8%A7M%C0!%E0%01%D0%F2Ev_%01%FC%23%AB%5E%F8%9AMJ7%B2%BD%D8%AC%EBq%3C%04%B2%12%A7%03%EF%83%DCr%2B%D7~%23~%C6Z%1F%C7%DF%84%9A6%87%E2%3DwV%D1%A6%B6%9D%EF%ED%24%D3%E4O)e*P%19Q%94%EF%00%16%C0%05N%09%E7%9A%8C579-7%3As%19%D3%A5I%A4%ED%D9z%9F%8C%D2j%11%B3d%9C%9CQ%5E%FB%F1W%FE%09%F5%F1g%E1%EF%8D.%F4m%2FMO%14%D8D%03%C3%A9Z%C9%1C%0B%22%9E%99I%1C%10%DFBG%3DO8%2B%D7%F62%EC%7C%97%3C%7B%9FaO%E1%ABM%0Ak1%03%CE%E5%23K%80e%94%9C%BE%C9rp09%0A%01%E3%D7%BF57%85%BC%0D%A3x%85%AE%A0%BC%B3R%8B%24%C1%0ApSl%AE%9C%7DB%82%7D%C94Q%5EF%23%E1O%CD%1E%E6%0B%E3k%C9%9A%3A%C6%9B%FF%00%08%3C%1E%1C%8FG%BB%B9%B4%FE%D2%D5%23%B5%9D%96N%7C%A1%C9Q%C7%19%C6%0F%B5%7Dw%F0%F6%EEK%EF%0CX%19NY-%89W%EA%D8%12%B2%81%93%DB%0A%3D%FA%D1Ew%60~%26c%99%7C%11%13%C6%96%16%B7%1A%ACm5%B430%85%40iP1%C6OsE%14W%ABvxvG%FF%D9"/>
   </div>
 
   <div style="margin-bottom:20px">
--- a/mobile/chrome/tests/browser_tapping.js
+++ b/mobile/chrome/tests/browser_tapping.js
@@ -30,24 +30,16 @@ function checkEvents(aEvents) {
       info("---- event check: failed match (" + aEvents[i] + " != " + gEvents[i] + "\n");
       return false;
     }
   }
   return true;
 }
 
 let gContextTypes = "";
-function dumpMessages(aMessage) {
-  if (aMessage.name == "Browser:ContextMenu") {
-    aMessage.json.types.forEach(function(aType) {
-      gContextTypes.push(aType);
-    })
-  }  
-}
-
 function clearContextTypes() {
   gContextTypes = [];
 
   if (ContextHelper.popupState)
     ContextHelper.hide();
 }
 
 function checkContextTypes(aTypes) {
@@ -62,30 +54,46 @@ function checkContextTypes(aTypes) {
       info("---- type check: failed match (" + aTypes[i] + ")\n");
       info("---- expected: [" + aTypes.join(",") + "] actual: [" + gContextTypes.join(",") + "]\n");
       return false;
     }
   }
   return true;
 }
 
+function waitForContextMenu(aCallback, aNextTest) {
+  clearContextTypes();
+
+  let browser = gCurrentTab.browser;
+  browser.messageManager.addMessageListener("Browser:ContextMenu", function(aMessage) {
+    browser.messageManager.removeMessageListener(aMessage.name, arguments.callee);
+    aMessage.json.types.forEach(function(aType) {
+      gContextTypes.push(aType);
+    });
+    setTimeout(function() {
+      aCallback(aMessage.json);
+      clearContextTypes();
+      aNextTest();
+    }, 0);
+  });
+}
 
 function test() {
   // The "runNextTest" approach is async, so we need to call "waitForExplicitFinish()"
   // We call "finish()" when the tests are finished
   waitForExplicitFinish();
 
   // Add new tab
   gCurrentTab = Browser.addTab(testURL, true);
   ok(gCurrentTab, "Tab Opened");
 
   window.addEventListener("TapSingle", dumpEvents, true);
   window.addEventListener("TapDouble", dumpEvents, true);
   window.addEventListener("TapLong", dumpEvents, true);
-  
+
   // Wait for the tab to load, then do the tests
   messageManager.addMessageListener("pageshow", function() {
   if (gCurrentTab.browser.currentURI.spec == testURL) {
     messageManager.removeMessageListener("pageshow", arguments.callee);
     runNextTest();
   }});
 }
 
@@ -115,24 +123,27 @@ gTests.push({
   desc: "Test the double tap behavior",
 
   run: function() {
     let browser = gCurrentTab.browser;
     let width = browser.getBoundingClientRect().width;
     let height = browser.getBoundingClientRect().height;
 
     // Should fire "TapSingle"
-    // XXX not working? WTF?
     info("Test good single tap");
     clearEvents();
     EventUtils.synthesizeMouse(browser, width / 2, height / 2, {});
-    todo(checkEvents(["TapSingle"]), "Fired a good single tap");
-    clearEvents();
 
-    setTimeout(function() { gCurrentTest.doubleTapTest(); }, 500);
+    // We wait a bit because of the delay allowed for double clicking on device
+    // where it is not native
+    setTimeout(function() {
+      ok(checkEvents(["TapSingle"]), "Fired a good single tap");
+      clearEvents();
+      gCurrentTest.doubleTapTest();
+    }, kDoubleClickInterval);
   },
 
   doubleTapTest: function() {
     let width = window.innerWidth;
     let height = window.innerHeight;
 
     // Should fire "TapDouble"
     info("Test good double tap");
@@ -168,17 +179,17 @@ gTests.push({
 
     info("Test a pan - non-tap event");
     clearEvents();
     EventUtils.synthesizeMouse(browser, width / 2, height / 4, { type: "mousedown" });
     EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mousemove" });
     EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mouseup" });
     ok(checkEvents([]), "Fired a pan which should be seen as a non event");
     clearEvents();
-    
+
     setTimeout(function() { gCurrentTest.longTapFailTest(); }, 500);
   },
 
   longTapFailTest: function() {
     let browser = gCurrentTab.browser;
     let width = browser.getBoundingClientRect().width;
     let height = browser.getBoundingClientRect().height;
 
@@ -195,81 +206,68 @@ gTests.push({
     }, 500);
   },
 
   longTapPassTest: function() {
     let browser = gCurrentTab.browser;
     let width = browser.getBoundingClientRect().width;
     let height = browser.getBoundingClientRect().height;
 
+    window.addEventListener("TapLong", function() {
+      window.removeEventListener("TapLong", arguments.callee, true);
+      EventUtils.synthesizeMouse(browser, width / 2, height / 4, { type: "mouseup" });
+      ok(checkEvents(["TapLong"]), "Fired a good long tap");
+      clearEvents();
+    }, true);
+
+    browser.messageManager.addMessageListener("Browser:ContextMenu", function(aMessage) {
+      browser.messageManager.removeMessageListener(aMessage.name, arguments.callee);
+      setTimeout(gCurrentTest.contextPlainLinkTest, 0);
+    });
+
     info("Test a good long pan");
     clearEvents();
     EventUtils.synthesizeMouse(browser, width / 2, height / 4, { type: "mousedown" });
-    setTimeout(function() {
-      EventUtils.synthesizeMouse(browser, width / 2, height / 4, { type: "mouseup" });
-      ok(checkEvents(["TapLong"]), "Fired a good long tap");
-      clearEvents();
-
-      gCurrentTest.contextPlainLinkTest();
-    }, 500);
   },
 
   contextPlainLinkTest: function() {
+    waitForContextMenu(function(aJSON) {
+      is(aJSON.linkTitle, "A blank page - nothing interesting", "Text content should be the content of the second link");
+      ok(checkContextTypes(["link","link-saveable","link-openable"]), "Plain link context types");
+    }, gCurrentTest.contextPlainImageTest);
+
     let browser = gCurrentTab.browser;
-    browser.messageManager.addMessageListener("Browser:ContextMenu", dumpMessages);
+    let linkDisabled = browser.contentDocument.getElementById("link-disabled");
+    let event = content.document.createEvent("PopupEvents");
+    event.initEvent("contextmenu", true, true);
+    linkDisabled.dispatchEvent(event);
 
     let link = browser.contentDocument.getElementById("link-single");
-    let linkRect = link.getBoundingClientRect();
-
-    clearContextTypes();
-    EventUtils.synthesizeMouseForContent(link, linkRect.width/2, linkRect.height/4, { type: "mousedown" }, window);
-    setTimeout(function() {
-      EventUtils.synthesizeMouseForContent(link, linkRect.width/2, linkRect.height/4, { type: "mouseup" }, window);
-      ok(checkContextTypes(["link","link-saveable","link-openable"]), "Plain link context types");
-      clearContextTypes();
-
-      gCurrentTest.contextPlainImageTest();
-    }, 500);
+    let event = content.document.createEvent("PopupEvents");
+    event.initEvent("contextmenu", true, true);
+    link.dispatchEvent(event);
   },
 
   contextPlainImageTest: function() {
-    let browser = gCurrentTab.browser;
-    browser.messageManager.addMessageListener("Browser:ContextMenu", dumpMessages);
-
-    let img = browser.contentDocument.getElementById("img-single");
-    let imgRect = img.getBoundingClientRect();
+    waitForContextMenu(function() {
+      ok(checkContextTypes(["image","image-shareable","image-loaded"]), "Plain image context types");
+    }, gCurrentTest.contextNestedImageTest);
 
-    clearContextTypes();
-    EventUtils.synthesizeMouseForContent(img, imgRect.width/2, imgRect.height/2, { type: "mousedown" }, window);
-    setTimeout(function() {
-      EventUtils.synthesizeMouseForContent(img, 1, 1, { type: "mouseup" }, window);
-      ok(checkContextTypes(["image","image-shareable","image-loaded"]), "Plain image context types");
-      clearContextTypes();
-
-      gCurrentTest.contextNestedImageTest();
-    }, 500);
+    let browser = gCurrentTab.browser;
+    let img = browser.contentDocument.getElementById("img-single");
+    let event = content.document.createEvent("PopupEvents");
+    event.initEvent("contextmenu", true, true);
+    img.dispatchEvent(event);
   },
 
   contextNestedImageTest: function() {
-    let browser = gCurrentTab.browser;
-    browser.messageManager.addMessageListener("Browser:ContextMenu", dumpMessages);
-
-    let img = browser.contentDocument.getElementById("img-nested");
-    let imgRect = img.getBoundingClientRect();
-
-    clearContextTypes();
-    EventUtils.synthesizeMouseForContent(img, 1, 1, { type: "mousedown" }, window);
-    setTimeout(function() {
-      EventUtils.synthesizeMouseForContent(img, 1, 1, { type: "mouseup" }, window);
+    waitForContextMenu(function() {
       ok(checkContextTypes(["link","link-saveable","image","image-shareable","image-loaded","link-openable"]), "Nested image context types");
-      clearContextTypes();
+    }, runNextTest);
 
-      gCurrentTest.lastTest();
-    }, 500);
-  },
-
-  lastTest: function() {
-    gCurrentTab.browser.messageManager.removeMessageListener("Browser:ContextMenu", dumpMessages);
-
-    runNextTest();
+    let browser = gCurrentTab.browser;
+    let img = browser.contentDocument.getElementById("img-nested");
+    let event = content.document.createEvent("PopupEvents");
+    event.initEvent("contextmenu", true, true);
+    img.dispatchEvent(event);
   }
 });