bug 1274809 - Add missing properties to OnClicked data, r?aswan draft 1274809-context-checked
authorTomislav Jovanovic <tomica@gmail.com>
Wed, 21 Sep 2016 02:12:11 +0200
changeset 416644 f7f0f306ef0b0cadfa3425c438570359a3177e26
parent 416562 f0e6cc6360213ba21fd98c887b55fce5c680df68
child 531908 5482e763541a751717eacd6f19384ba99a1bd9da
push id30202
push userbmo:tomica@gmail.com
push dateThu, 22 Sep 2016 18:21:17 +0000
reviewersaswan
bugs1274809
milestone52.0a1
bug 1274809 - Add missing properties to OnClicked data, r?aswan MozReview-Commit-ID: 4BodpIuKCz2
browser/components/extensions/ext-contextMenus.js
browser/components/extensions/test/browser/browser_ext_contextMenus.js
browser/components/extensions/test/browser/browser_ext_contextMenus_checkboxes.js
browser/components/extensions/test/browser/browser_ext_contextMenus_radioGroups.js
browser/components/extensions/test/browser/context.html
--- a/browser/components/extensions/ext-contextMenus.js
+++ b/browser/components/extensions/ext-contextMenus.js
@@ -169,33 +169,34 @@ var gMenuBuilder = {
     if (!item.enabled) {
       element.setAttribute("disabled", "true");
     }
 
     element.addEventListener("command", event => {  // eslint-disable-line mozilla/balanced-listeners
       if (event.target !== event.currentTarget) {
         return;
       }
+      const wasChecked = item.checked;
       if (item.type == "checkbox") {
         item.checked = !item.checked;
       } else if (item.type == "radio") {
         // Deselect all radio items in the current radio group.
         for (let child of item.parent.children) {
           if (child.type == "radio" && child.groupName == item.groupName) {
             child.checked = false;
           }
         }
         // Select the clicked radio item.
         item.checked = true;
       }
 
       item.tabManager.addActiveTabPermission();
 
       let tab = item.tabManager.convert(contextData.tab);
-      let info = item.getClickInfo(contextData, event);
+      let info = item.getClickInfo(contextData, wasChecked);
       item.extension.emit("webext-contextmenu-menuitem-click", info, tab);
       if (item.onclick) {
         runSafe(item.extContext, item.onclick, info, tab);
       }
     });
 
     return element;
   },
@@ -394,46 +395,51 @@ MenuItem.prototype = {
 
     let menuMap = gContextMenuMap.get(this.extension);
     menuMap.delete(this.id);
     if (this.root == this) {
       gRootItems.delete(this.extension);
     }
   },
 
-  getClickInfo(contextData, event) {
+  getClickInfo(contextData, wasChecked) {
     let mediaType;
     if (contextData.onVideo) {
       mediaType = "video";
     }
     if (contextData.onAudio) {
       mediaType = "audio";
     }
     if (contextData.onImage) {
       mediaType = "image";
     }
 
     let info = {
       menuItemId: this.id,
+      editable: contextData.onEditableArea,
     };
 
     function setIfDefined(argName, value) {
-      if (value) {
+      if (value !== undefined) {
         info[argName] = value;
       }
     }
 
     setIfDefined("parentMenuItemId", this.parentId);
     setIfDefined("mediaType", mediaType);
     setIfDefined("linkUrl", contextData.linkUrl);
     setIfDefined("srcUrl", contextData.srcUrl);
     setIfDefined("pageUrl", contextData.pageUrl);
     setIfDefined("frameUrl", contextData.frameUrl);
     setIfDefined("selectionText", contextData.selectionText);
-    setIfDefined("editable", contextData.onEditableArea);
+
+    if ((this.type === "checkbox") || (this.type === "radio")) {
+      info.checked = this.checked;
+      info.wasChecked = wasChecked;
+    }
 
     return info;
   },
 
   enabledForContext(contextData) {
     let contexts = getContexts(contextData);
     if (!this.contexts.some(n => contexts.has(n))) {
       return false;
--- a/browser/components/extensions/test/browser/browser_ext_contextMenus.js
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus.js
@@ -65,17 +65,17 @@ add_task(function* () {
         browser.test.sendMessage("browser.contextMenus.onClicked", {info, tab});
       });
 
       browser.contextMenus.create({
         contexts: ["all"],
         type: "separator",
       });
 
-      let contexts = ["page", "selection", "image"];
+      let contexts = ["page", "selection", "image", "editable"];
       for (let i = 0; i < contexts.length; i++) {
         let context = contexts[i];
         let title = context;
         browser.contextMenus.create({
           title: title,
           contexts: [context],
           id: "ext-" + context,
           onclick: genericOnClick,
@@ -139,22 +139,23 @@ add_task(function* () {
   yield extension.startup();
   yield extension.awaitFinish("contextmenus");
 
   let expectedClickInfo = {
     menuItemId: "ext-image",
     mediaType: "image",
     srcUrl: "http://mochi.test:8888/browser/browser/components/extensions/test/browser/ctxmenu-image.png",
     pageUrl: "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html",
+    editable: false,
   };
 
   function checkClickInfo(result) {
     for (let i of Object.keys(expectedClickInfo)) {
       is(result.info[i], expectedClickInfo[i],
-         "click info " + i + " expected to be: " + expectedClickInfo[i] + " but was: " + info[i]);
+         "click info " + i + " expected to be: " + expectedClickInfo[i] + " but was: " + result.info[i]);
     }
     is(expectedClickInfo.pageSrc, result.tab.url);
   }
 
   let extensionMenuRoot = yield openExtensionContextMenu();
 
   // Check some menu items
   let items = extensionMenuRoot.getElementsByAttribute("label", "image");
@@ -175,16 +176,40 @@ add_task(function* () {
   // Click on ext-image item and check the click results
   yield closeExtensionContextMenu(image);
 
   let result = yield extension.awaitMessage("onclick");
   checkClickInfo(result);
   result = yield extension.awaitMessage("browser.contextMenus.onClicked");
   checkClickInfo(result);
 
+
+  // Test "editable" context and OnClick data property.
+  extensionMenuRoot = yield openExtensionContextMenu("#edit-me");
+
+  // Check some menu items.
+  items = extensionMenuRoot.getElementsByAttribute("label", "editable");
+  is(items.length, 1, "contextMenu item for text input element was found (context=editable)");
+  let editable = items[0];
+
+  // Click on ext-editable item and check the click results.
+  yield closeExtensionContextMenu(editable);
+
+  expectedClickInfo = {
+    menuItemId: "ext-editable",
+    pageUrl: "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html",
+    editable: true,
+  };
+
+  result = yield extension.awaitMessage("onclick");
+  checkClickInfo(result);
+  result = yield extension.awaitMessage("browser.contextMenus.onClicked");
+  checkClickInfo(result);
+
+
   // Select some text
   yield ContentTask.spawn(gBrowser.selectedBrowser, { }, function* (arg) {
     let doc = content.document;
     let range = doc.createRange();
     let selection = content.getSelection();
     selection.removeAllRanges();
     let textNode = doc.getElementById("img1").previousSibling;
     range.setStart(textNode, 0);
--- a/browser/components/extensions/test/browser/browser_ext_contextMenus_checkboxes.js
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus_checkboxes.js
@@ -9,16 +9,21 @@ add_task(function* () {
   gBrowser.selectedTab = tab1;
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["contextMenus"],
     },
 
     background: function() {
+      // Report onClickData info back.
+      browser.contextMenus.onClicked.addListener(info => {
+        browser.test.sendMessage("contextmenus-click", info);
+      });
+
       browser.contextMenus.create({
         title: "Checkbox",
         type: "checkbox",
       });
 
       browser.contextMenus.create({
         type: "separator",
       });
@@ -48,27 +53,44 @@ add_task(function* () {
 
     is(checkboxItems[0].hasAttribute("checked"), expectedStates[0], `checkbox item 1 has state (checked=${expectedStates[0]})`);
     is(checkboxItems[1].hasAttribute("checked"), expectedStates[1], `checkbox item 2 has state (checked=${expectedStates[1]})`);
     is(checkboxItems[2].hasAttribute("checked"), expectedStates[2], `checkbox item 3 has state (checked=${expectedStates[2]})`);
 
     return extensionMenuRoot.getElementsByAttribute("type", "checkbox");
   }
 
+  function confirmOnClickData(onClickData, id, was, checked) {
+    is(onClickData.wasChecked, was, `checkbox item ${id} was ${was ? "" : "not "}checked before the click`);
+    is(onClickData.checked, checked, `checkbox item ${id} is ${checked ? "" : "not "}checked after the click`);
+  }
+
   let extensionMenuRoot = yield openExtensionContextMenu();
   let items = confirmCheckboxStates(extensionMenuRoot, [false, true, false]);
   yield closeExtensionContextMenu(items[0]);
 
+  let result = yield extension.awaitMessage("contextmenus-click");
+  confirmOnClickData(result, 1, false, true);
+
   extensionMenuRoot = yield openExtensionContextMenu();
   items = confirmCheckboxStates(extensionMenuRoot, [true, true, false]);
   yield closeExtensionContextMenu(items[2]);
 
+  result = yield extension.awaitMessage("contextmenus-click");
+  confirmOnClickData(result, 3, false, true);
+
   extensionMenuRoot = yield openExtensionContextMenu();
   items = confirmCheckboxStates(extensionMenuRoot, [true, true, true]);
   yield closeExtensionContextMenu(items[0]);
 
+  result = yield extension.awaitMessage("contextmenus-click");
+  confirmOnClickData(result, 1, true, false);
+
   extensionMenuRoot = yield openExtensionContextMenu();
   items = confirmCheckboxStates(extensionMenuRoot, [false, true, true]);
   yield closeExtensionContextMenu(items[2]);
 
+  result = yield extension.awaitMessage("contextmenus-click");
+  confirmOnClickData(result, 3, true, false);
+
   yield extension.unload();
   yield BrowserTestUtils.removeTab(tab1);
 });
--- a/browser/components/extensions/test/browser/browser_ext_contextMenus_radioGroups.js
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus_radioGroups.js
@@ -9,16 +9,21 @@ add_task(function* () {
   gBrowser.selectedTab = tab1;
 
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["contextMenus"],
     },
 
     background: function() {
+      // Report onClickData info back.
+      browser.contextMenus.onClicked.addListener(info => {
+        browser.test.sendMessage("contextmenus-click", info);
+      });
+
       browser.contextMenus.create({
         title: "radio-group-1",
         type: "radio",
         checked: true,
       });
 
       browser.contextMenus.create({
         type: "separator",
@@ -52,27 +57,44 @@ add_task(function* () {
 
     is(radioGroup1[0].hasAttribute("checked"), expectedStates[0], `radio item 1 has state (checked=${expectedStates[0]})`);
     is(radioGroup2[0].hasAttribute("checked"), expectedStates[1], `radio item 2 has state (checked=${expectedStates[1]})`);
     is(radioGroup2[1].hasAttribute("checked"), expectedStates[2], `radio item 3 has state (checked=${expectedStates[2]})`);
 
     return extensionMenuRoot.getElementsByAttribute("type", "radio");
   }
 
+  function confirmOnClickData(onClickData, id, was, checked) {
+    is(onClickData.wasChecked, was, `radio item ${id} was ${was ? "" : "not "}checked before the click`);
+    is(onClickData.checked, checked, `radio item ${id} is ${checked ? "" : "not "}checked after the click`);
+  }
+
   let extensionMenuRoot = yield openExtensionContextMenu();
   let items = confirmRadioGroupStates(extensionMenuRoot, [true, false, false]);
   yield closeExtensionContextMenu(items[1]);
 
+  let result = yield extension.awaitMessage("contextmenus-click");
+  confirmOnClickData(result, 2, false, true);
+
   extensionMenuRoot = yield openExtensionContextMenu();
   items = confirmRadioGroupStates(extensionMenuRoot, [true, true, false]);
   yield closeExtensionContextMenu(items[2]);
 
-  extensionMenuRoot = yield openExtensionContextMenu();
-  items = confirmRadioGroupStates(extensionMenuRoot, [true, false, true]);
-  yield closeExtensionContextMenu(items[0]);
+  result = yield extension.awaitMessage("contextmenus-click");
+  confirmOnClickData(result, 3, false, true);
 
   extensionMenuRoot = yield openExtensionContextMenu();
   items = confirmRadioGroupStates(extensionMenuRoot, [true, false, true]);
   yield closeExtensionContextMenu(items[0]);
 
+  result = yield extension.awaitMessage("contextmenus-click");
+  confirmOnClickData(result, 1, true, true);
+
+  extensionMenuRoot = yield openExtensionContextMenu();
+  items = confirmRadioGroupStates(extensionMenuRoot, [true, false, true]);
+  yield closeExtensionContextMenu(items[0]);
+
+  result = yield extension.awaitMessage("contextmenus-click");
+  confirmOnClickData(result, 1, true, true);
+
   yield extension.unload();
   yield BrowserTestUtils.removeTab(tab1);
 });
--- a/browser/components/extensions/test/browser/context.html
+++ b/browser/components/extensions/test/browser/context.html
@@ -9,10 +9,14 @@
     <a href="some-link" id="link1">Some link</a>
   </p>
 
   <p>
     <a href="image-around-some-link">
       <img src="ctxmenu-image.png" id="img-wrapped-in-link">
     </a>
   </p>
+
+  <p>
+    <input type="text" id="edit-me">
+  </p>
   </body>
 </html>