dom/browser-element/mochitest/browserElement_ContextmenuEvents.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/browser-element/mochitest/browserElement_ContextmenuEvents.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,289 @@
     1.4 +'use strict';
     1.5 +
     1.6 +SimpleTest.waitForExplicitFinish();
     1.7 +browserElementTestHelpers.setEnabledPref(true);
     1.8 +browserElementTestHelpers.addPermission();
     1.9 +
    1.10 +let audioUrl = 'http://mochi.test:8888/tests/dom/browser-element/mochitest/audio.ogg';
    1.11 +let videoUrl = 'http://mochi.test:8888/tests/dom/browser-element/mochitest/short-video.ogv';
    1.12 +
    1.13 +function runTests() {
    1.14 +  createIframe(function onIframeLoaded() {
    1.15 +    checkEmptyContextMenu();
    1.16 +  });
    1.17 +}
    1.18 +
    1.19 +function checkEmptyContextMenu() {
    1.20 +  sendContextMenuTo('body', function onContextMenu(detail) {
    1.21 +    is(detail.contextmenu, null, 'Body context clicks have no context menu');
    1.22 +
    1.23 +    checkInnerContextMenu();
    1.24 +  });
    1.25 +}
    1.26 +
    1.27 +function checkInnerContextMenu() {
    1.28 +  sendContextMenuTo('#inner-link', function onContextMenu(detail) {
    1.29 +    is(detail.systemTargets.length, 1, 'Includes anchor data');
    1.30 +    is(detail.contextmenu.items.length, 2, 'Inner clicks trigger correct menu');
    1.31 +
    1.32 +    checkCustomContextMenu();
    1.33 +  });
    1.34 +}
    1.35 +
    1.36 +function checkCustomContextMenu() {
    1.37 +  sendContextMenuTo('#menu1-trigger', function onContextMenu(detail) {
    1.38 +    is(detail.contextmenu.items.length, 2, 'trigger custom contextmenu');
    1.39 +
    1.40 +    checkNestedContextMenu();
    1.41 +  });
    1.42 +}
    1.43 +
    1.44 +function checkNestedContextMenu() {
    1.45 +  sendContextMenuTo('#menu2-trigger', function onContextMenu(detail) {
    1.46 +    var innerMenu = detail.contextmenu.items.filter(function(x) {
    1.47 +      return x.type === 'menu';
    1.48 +    });
    1.49 +    is(detail.systemTargets.length, 2, 'Includes anchor and img data');
    1.50 +    ok(innerMenu.length > 0, 'Menu contains a nested menu');
    1.51 +
    1.52 +    checkPreviousContextMenuHandler();
    1.53 +  });
    1.54 +}
    1.55 +
    1.56 + // Finished testing the data passed to the contextmenu handler,
    1.57 + // now we start selecting contextmenu items
    1.58 +function checkPreviousContextMenuHandler() {
    1.59 +  // This is previously triggered contextmenu data, since we have
    1.60 +  // fired subsequent contextmenus this should not be mistaken
    1.61 +  // for a current menuitem
    1.62 +  var detail = previousContextMenuDetail;
    1.63 +  var previousId = detail.contextmenu.items[0].id;
    1.64 +  checkContextMenuCallbackForId(detail, previousId, function onCallbackFired(label) {
    1.65 +    is(label, null, 'Callback label should be empty since this handler is old');
    1.66 +
    1.67 +    checkCurrentContextMenuHandler();
    1.68 +  });
    1.69 +}
    1.70 +
    1.71 +function checkCurrentContextMenuHandler() {
    1.72 +  // This triggers a current menuitem
    1.73 +  var detail = currentContextMenuDetail;
    1.74 +
    1.75 +  var innerMenu = detail.contextmenu.items.filter(function(x) {
    1.76 +    return x.type === 'menu';
    1.77 +  });
    1.78 +
    1.79 +  var currentId = innerMenu[0].items[1].id;
    1.80 +  checkContextMenuCallbackForId(detail, currentId, function onCallbackFired(label) {
    1.81 +    is(label, 'inner 2', 'Callback label should be set correctly');
    1.82 +
    1.83 +    checkAgainCurrentContextMenuHandler();
    1.84 +  });
    1.85 +}
    1.86 +
    1.87 +function checkAgainCurrentContextMenuHandler() {
    1.88 +  // Once an item it selected, subsequent selections are ignored
    1.89 +  var detail = currentContextMenuDetail;
    1.90 +
    1.91 +  var innerMenu = detail.contextmenu.items.filter(function(x) {
    1.92 +    return x.type === 'menu';
    1.93 +  });
    1.94 +
    1.95 +  var currentId = innerMenu[0].items[1].id;
    1.96 +  checkContextMenuCallbackForId(detail, currentId, function onCallbackFired(label) {
    1.97 +    is(label, null, 'Callback label should be empty since this handler has already been used');
    1.98 +
    1.99 +    checkCallbackWithPreventDefault();
   1.100 +  });
   1.101 +};
   1.102 +
   1.103 +// Finished testing callbacks if the embedder calls preventDefault() on the
   1.104 +// mozbrowsercontextmenu event, now we start checking for some cases where the embedder
   1.105 +// does not want to call preventDefault() for some reasons.
   1.106 +function checkCallbackWithPreventDefault() {
   1.107 +  sendContextMenuTo('#menu1-trigger', function onContextMenu(detail) {
   1.108 +    var id = detail.contextmenu.items[0].id;
   1.109 +    checkContextMenuCallbackForId(detail, id, function onCallbackFired(label) {
   1.110 +      is(label, 'foo', 'Callback label should be set correctly');
   1.111 +
   1.112 +      checkCallbackWithoutPreventDefault();
   1.113 +    });
   1.114 +  });
   1.115 +}
   1.116 +
   1.117 +function checkCallbackWithoutPreventDefault() {
   1.118 +  sendContextMenuTo('#menu1-trigger', function onContextMenu(detail) {
   1.119 +    var id = detail.contextmenu.items[0].id;
   1.120 +    checkContextMenuCallbackForId(detail, id, function onCallbackFired(label) {
   1.121 +      is(label, null, 'Callback label should be null');
   1.122 +
   1.123 +      checkImageContextMenu();
   1.124 +    });
   1.125 +  }, /* ignorePreventDefault */ true);
   1.126 +}
   1.127 +
   1.128 +function checkImageContextMenu() {
   1.129 +  sendContextMenuTo('#menu3-trigger', function onContextMenu(detail) {
   1.130 +    var target = detail.systemTargets[0];
   1.131 +    is(target.nodeName, 'IMG', 'Reports correct nodeName');
   1.132 +    is(target.data.uri, 'example.png', 'Reports correct uri');
   1.133 +
   1.134 +    checkVideoContextMenu();
   1.135 +  }, /* ignorePreventDefault */ true);
   1.136 +}
   1.137 +
   1.138 +function checkVideoContextMenu() {
   1.139 +  sendContextMenuTo('#menu4-trigger', function onContextMenu(detail) {
   1.140 +    var target = detail.systemTargets[0];
   1.141 +    is(target.nodeName, 'VIDEO', 'Reports correct nodeName');
   1.142 +    is(target.data.uri, videoUrl, 'Reports uri correctly in data');
   1.143 +    is(target.data.hasVideo, true, 'Video data in video tag does "hasVideo"');
   1.144 +
   1.145 +    checkAudioContextMenu();
   1.146 +  }, /* ignorePreventDefault */ true);
   1.147 +}
   1.148 +
   1.149 +function checkAudioContextMenu() {
   1.150 +  sendContextMenuTo('#menu6-trigger', function onContextMenu(detail) {
   1.151 +    var target = detail.systemTargets[0];
   1.152 +    is(target.nodeName, 'AUDIO', 'Reports correct nodeName');
   1.153 +    is(target.data.uri, audioUrl, 'Reports uri correctly in data');
   1.154 +
   1.155 +    checkAudioinVideoContextMenu();
   1.156 +  }, /* ignorePreventDefault */ true);
   1.157 +}
   1.158 +
   1.159 +function checkAudioinVideoContextMenu() {
   1.160 +  sendSrcTo('#menu5-trigger', audioUrl, function onSrcSet() {
   1.161 +    sendContextMenuTo('#menu5-trigger', function onContextMenu(detail) {
   1.162 +      var target = detail.systemTargets[0];
   1.163 +      is(target.nodeName, 'VIDEO', 'Reports correct nodeName');
   1.164 +      is(target.data.uri, audioUrl, 'Reports uri correctly in data');
   1.165 +      is(target.data.hasVideo, false, 'Audio data in video tag reports no "hasVideo"');
   1.166 +
   1.167 +      SimpleTest.finish();
   1.168 +    }, /* ignorePreventDefault */ true);
   1.169 +  });
   1.170 +}
   1.171 +
   1.172 +/* Helpers */
   1.173 +var mm = null;
   1.174 +var previousContextMenuDetail = null;
   1.175 +var currentContextMenuDetail = null;
   1.176 +
   1.177 +function sendSrcTo(selector, src, callback) {
   1.178 +  mm.sendAsyncMessage('setsrc', { 'selector': selector, 'src': src });
   1.179 +  mm.addMessageListener('test:srcset', function onSrcSet(msg) {
   1.180 +    mm.removeMessageListener('test:srcset', onSrcSet);
   1.181 +    callback();
   1.182 +  });
   1.183 +}
   1.184 +
   1.185 +function sendContextMenuTo(selector, callback, ignorePreventDefault) {
   1.186 +  iframe.addEventListener('mozbrowsercontextmenu', function oncontextmenu(e) {
   1.187 +    iframe.removeEventListener(e.type, oncontextmenu);
   1.188 +
   1.189 +    // The embedder should call preventDefault() on the event if it will handle
   1.190 +    // it. Not calling preventDefault() means it won't handle the event and 
   1.191 +    // should not be able to deal with context menu callbacks.
   1.192 +    if (ignorePreventDefault !== true) {
   1.193 +      e.preventDefault();
   1.194 +    }
   1.195 +
   1.196 +    // Keep a reference to previous/current contextmenu event details.
   1.197 +    previousContextMenuDetail = currentContextMenuDetail;
   1.198 +    currentContextMenuDetail = e.detail;
   1.199 +
   1.200 +    setTimeout(function() { callback(e.detail); });
   1.201 +  });
   1.202 +
   1.203 +  mm.sendAsyncMessage('contextmenu', { 'selector': selector });
   1.204 +}
   1.205 +
   1.206 +function checkContextMenuCallbackForId(detail, id, callback) {
   1.207 +  mm.addMessageListener('test:callbackfired', function onCallbackFired(msg) {
   1.208 +    mm.removeMessageListener('test:callbackfired', onCallbackFired);
   1.209 +
   1.210 +    msg = SpecialPowers.wrap(msg);
   1.211 +    setTimeout(function() { callback(msg.data.label); });
   1.212 +  });
   1.213 +
   1.214 +  detail.contextMenuItemSelected(id);
   1.215 +}
   1.216 +
   1.217 +
   1.218 +var iframe = null;
   1.219 +function createIframe(callback) {
   1.220 +  iframe = document.createElement('iframe');
   1.221 +  SpecialPowers.wrap(iframe).mozbrowser = true;
   1.222 +
   1.223 +  iframe.src = 'data:text/html,<html>' +
   1.224 +    '<body>' +
   1.225 +    '<menu type="context" id="menu1" label="firstmenu">' +
   1.226 +      '<menuitem label="foo" onclick="window.onContextMenuCallbackFired(event)"></menuitem>' +
   1.227 +      '<menuitem label="bar" onclick="window.onContextMenuCallbackFired(event)"></menuitem>' +
   1.228 +    '</menu>' +
   1.229 +    '<menu type="context" id="menu2" label="secondmenu">' +
   1.230 +      '<menuitem label="outer" onclick="window.onContextMenuCallbackFired(event)"></menuitem>' +
   1.231 +      '<menu>' +
   1.232 +        '<menuitem label="inner 1"></menuitem>' +
   1.233 +        '<menuitem label="inner 2" onclick="window.onContextMenuCallbackFired(event)"></menuitem>' +
   1.234 +      '</menu>' +
   1.235 +    '</menu>' +
   1.236 +    '<div id="menu1-trigger" contextmenu="menu1"><a id="inner-link" href="foo.html">Menu 1</a></div>' +
   1.237 +    '<a href="bar.html" contextmenu="menu2"><img id="menu2-trigger" src="example.png" /></a>' +
   1.238 +    '<img id="menu3-trigger" src="example.png" />' +
   1.239 +    '<video id="menu4-trigger" src="' + videoUrl + '"></video>' +
   1.240 +    '<video id="menu5-trigger" preload="metadata"></video>' +
   1.241 +    '<audio id="menu6-trigger" src="' + audioUrl + '"></audio>' +
   1.242 +    '</body></html>';
   1.243 +  document.body.appendChild(iframe);
   1.244 +
   1.245 +  // The following code will be included in the child
   1.246 +  // =========================================================================
   1.247 +  function iframeScript() {
   1.248 +    addMessageListener('contextmenu', function onContextMenu(msg) {
   1.249 +      var document = content.document;
   1.250 +      var evt = document.createEvent('HTMLEvents');
   1.251 +      evt.initEvent('contextmenu', true, true);
   1.252 +      document.querySelector(msg.data.selector).dispatchEvent(evt);
   1.253 +    });
   1.254 +
   1.255 +    addMessageListener('setsrc', function onContextMenu(msg) {
   1.256 +      var wrappedTarget = content.document.querySelector(msg.data.selector);
   1.257 +      var target = XPCNativeWrapper.unwrap(wrappedTarget);
   1.258 +      target.addEventListener('loadedmetadata', function() {
   1.259 +        sendAsyncMessage('test:srcset');
   1.260 +      });
   1.261 +      target.src = msg.data.src;
   1.262 +    });
   1.263 +
   1.264 +    addMessageListener('browser-element-api:call', function onCallback(msg) {
   1.265 +      if (msg.data.msg_name != 'fire-ctx-callback')
   1.266 +        return;
   1.267 +
   1.268 +      /* Use setTimeout in order to react *after* the platform */
   1.269 +      content.setTimeout(function() {
   1.270 +        sendAsyncMessage('test:callbackfired', { label: label });
   1.271 +        label = null;
   1.272 +      });
   1.273 +    });
   1.274 +
   1.275 +    var label = null;
   1.276 +    XPCNativeWrapper.unwrap(content).onContextMenuCallbackFired = function(e) {
   1.277 +      label = e.target.getAttribute('label');
   1.278 +    };
   1.279 +  }
   1.280 +  // =========================================================================
   1.281 +
   1.282 +  iframe.addEventListener('mozbrowserloadend', function onload(e) {
   1.283 +    iframe.removeEventListener(e.type, onload);
   1.284 +    mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
   1.285 +    mm.loadFrameScript('data:,(' + iframeScript.toString() + ')();', false);
   1.286 +
   1.287 +    // Now we're ready, let's start testing.
   1.288 +    callback();
   1.289 +  });
   1.290 +}
   1.291 +
   1.292 +addEventListener('testready', runTests);

mercurial