content/base/test/test_object.html

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 <!DOCTYPE html>
michael@0 2 <html>
michael@0 3 <head>
michael@0 4 <title>Plugin instantiation</title>
michael@0 5 <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
michael@0 6 <script type="application/javascript" src="/tests/SimpleTest/SpecialPowers.js"></script>
michael@0 7 <meta charset="utf-8">
michael@0 8 <body onload="onLoad()">
michael@0 9 <script class="testbody" type="text/javascript;version=1.8">
michael@0 10
michael@0 11 "use strict";
michael@0 12 SimpleTest.waitForExplicitFinish();
michael@0 13
michael@0 14 var pluginHost = SpecialPowers.Cc["@mozilla.org/plugin/host;1"]
michael@0 15 .getService(SpecialPowers.Ci.nsIPluginHost);
michael@0 16 var pluginTags = pluginHost.getPluginTags();
michael@0 17 for (var tag of pluginTags) {
michael@0 18 if (tag.name == "Test Plug-in") {
michael@0 19 tag.enabledState = SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED;;
michael@0 20 }
michael@0 21 }
michael@0 22
michael@0 23 // This can go away once embed also is on WebIDL
michael@0 24 let OBJLC = SpecialPowers.Ci.nsIObjectLoadingContent;
michael@0 25
michael@0 26 // Use string modes in this test to make the test easier to read/debug.
michael@0 27 // nsIObjectLoadingContent refers to this as "type", but I am using "mode"
michael@0 28 // in the test to avoid confusing with content-type.
michael@0 29 let prettyModes = {};
michael@0 30 prettyModes[OBJLC.TYPE_LOADING] = "loading";
michael@0 31 prettyModes[OBJLC.TYPE_IMAGE] = "image";
michael@0 32 prettyModes[OBJLC.TYPE_PLUGIN] = "plugin";
michael@0 33 prettyModes[OBJLC.TYPE_DOCUMENT] = "document";
michael@0 34 prettyModes[OBJLC.TYPE_NULL] = "none";
michael@0 35
michael@0 36 let body = document.body;
michael@0 37 // A single-pixel white png
michael@0 38 let testPNG = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AoIFiETNqbNRQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAACklEQVQIHWP4DwABAQEANl9ngAAAAABJRU5ErkJggg==';
michael@0 39 // An empty, but valid, SVG
michael@0 40 let testSVG = 'data:image/svg+xml,<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"></svg>';
michael@0 41 // executeSoon wrapper to count pending callbacks
michael@0 42 let pendingCalls = 0;
michael@0 43 let afterPendingCalls = false;
michael@0 44
michael@0 45 function runWhenDone(func) {
michael@0 46 if (!pendingCalls)
michael@0 47 func();
michael@0 48 else
michael@0 49 afterPendingCalls = func;
michael@0 50 }
michael@0 51 function runSoon(func) {
michael@0 52 pendingCalls++;
michael@0 53 SimpleTest.executeSoon(function() {
michael@0 54 func();
michael@0 55 if (--pendingCalls < 1 && afterPendingCalls)
michael@0 56 afterPendingCalls();
michael@0 57 });
michael@0 58 }
michael@0 59 function src(obj, state, uri) {
michael@0 60 // If we have a running plugin, src changing should always throw it out,
michael@0 61 // even if it causes us to load the same plugin again.
michael@0 62 if (uri && runningPlugin(obj, state)) {
michael@0 63 if (!state.oldPlugins)
michael@0 64 state.oldPlugins = [];
michael@0 65 try {
michael@0 66 state.oldPlugins.push(obj.getObjectValue());
michael@0 67 } catch (e) {
michael@0 68 ok(false, "Running plugin but cannot call getObjectValue?");
michael@0 69 }
michael@0 70 }
michael@0 71
michael@0 72 var srcattr;
michael@0 73 if (state.tagName == "object")
michael@0 74 srcattr = "data";
michael@0 75 else if (state.tagName == "embed")
michael@0 76 srcattr = "src";
michael@0 77 else
michael@0 78 ok(false, "Internal test fail: Why are we setting the src of an applet");
michael@0 79
michael@0 80 // Plugins should always go away immediately on src/data change
michael@0 81 state.initialPlugin = false;
michael@0 82 if (uri === null) {
michael@0 83 removeAttr(obj, srcattr);
michael@0 84 // TODO Bug 767631 - we don't trigger loadObject on UnsetAttr :(
michael@0 85 forceReload(obj, state);
michael@0 86 } else {
michael@0 87 setAttr(obj, srcattr, uri);
michael@0 88 }
michael@0 89 }
michael@0 90 // We have to be careful not to reach past the nsObjectLoadingContent
michael@0 91 // prototype to touch generic element attributes, as this will try to
michael@0 92 // spawn the plugin, breaking our ability to test for that.
michael@0 93 function getAttr(obj, attr) {
michael@0 94 return document.body.constructor.prototype.getAttribute.call(obj, attr);
michael@0 95 }
michael@0 96 function setAttr(obj, attr, val) {
michael@0 97 return document.body.constructor.prototype.setAttribute.call(obj, attr, val);
michael@0 98 }
michael@0 99 function hasAttr(obj, attr) {
michael@0 100 return document.body.constructor.prototype.hasAttribute.call(obj, attr);
michael@0 101 }
michael@0 102 function removeAttr(obj, attr) {
michael@0 103 return document.body.constructor.prototype.removeAttribute.call(obj, attr);
michael@0 104 }
michael@0 105 function setDisplayed(obj, display) {
michael@0 106 if (display)
michael@0 107 removeAttr(obj, 'style');
michael@0 108 else
michael@0 109 setAttr(obj, 'style', "display: none;");
michael@0 110 }
michael@0 111 function displayed(obj) {
michael@0 112 // Hacky, but that's all we use style for.
michael@0 113 return !hasAttr(obj, 'style');
michael@0 114 }
michael@0 115 function actualType(obj, state) {
michael@0 116 return state.getActualType.call(obj);
michael@0 117 }
michael@0 118 function getMode(obj, state) {
michael@0 119 return prettyModes[state.getDisplayedType.call(obj)];
michael@0 120 }
michael@0 121 function runningPlugin(obj, state) {
michael@0 122 return state.getHasRunningPlugin.call(obj);
michael@0 123 }
michael@0 124
michael@0 125 // TODO this is hacky and might hide some failures, but is needed until
michael@0 126 // Bug 767635 lands -- which itself will probably mean tweaking this test.
michael@0 127 function forceReload(obj, state) {
michael@0 128 let attr;
michael@0 129 if (state.tagName == "object")
michael@0 130 attr = "data";
michael@0 131 else if (state.tagName == "embed")
michael@0 132 attr = "src";
michael@0 133
michael@0 134 if (attr && hasAttr(obj, attr)) {
michael@0 135 src(obj, state, getAttr(obj, attr));
michael@0 136 } else if (body.contains(obj)) {
michael@0 137 body.appendChild(obj);
michael@0 138 } else {
michael@0 139 // Out of document nodes without data attributes simply can't be
michael@0 140 // reloaded currently. Bug 767635
michael@0 141 }
michael@0 142 };
michael@0 143
michael@0 144 // Make a list of combinations of sub-lists, e.g.:
michael@0 145 // [ [a, b], [c, d] ]
michael@0 146 // ->
michael@0 147 // [ [a, c], [a, d], [b, c], [b, d] ]
michael@0 148 function eachList() {
michael@0 149 let all = [];
michael@0 150 if (!arguments.length)
michael@0 151 return all;
michael@0 152 let list = Array.prototype.slice.call(arguments, 0);
michael@0 153 for (let c of list[0]) {
michael@0 154 if (list.length > 1) {
michael@0 155 for (let x of eachList.apply(this,list.slice(1))) {
michael@0 156 all.push((c.length ? [c] : []).concat(x));
michael@0 157 }
michael@0 158 } else if (c.length) {
michael@0 159 all.push([c]);
michael@0 160 }
michael@0 161 }
michael@0 162 return all;
michael@0 163 }
michael@0 164
michael@0 165 let states = {
michael@0 166 svg: function(obj, state) {
michael@0 167 removeAttr(obj, "type");
michael@0 168 src(obj, state, testSVG);
michael@0 169 state.noChannel = false;
michael@0 170 state.expectedType = "image/svg";
michael@0 171 // SVGs are actually image-like subdocuments
michael@0 172 state.expectedMode = "document";
michael@0 173 },
michael@0 174 image: function(obj, state) {
michael@0 175 removeAttr(obj, "type");
michael@0 176 src(obj, state, testPNG);
michael@0 177 state.noChannel = false;
michael@0 178 state.expectedMode = "image";
michael@0 179 state.expectedType = "image/png";
michael@0 180 },
michael@0 181 plugin: function(obj, state) {
michael@0 182 removeAttr(obj, "type");
michael@0 183 src(obj, state, "data:application/x-test,foo");
michael@0 184 state.noChannel = false;
michael@0 185 state.expectedType = "application/x-test";
michael@0 186 state.expectedMode = "plugin";
michael@0 187 },
michael@0 188 pluginExtension: function(obj, state) {
michael@0 189 src(obj, state, "./fake_plugin.tst");
michael@0 190 state.expectedMode = "plugin";
michael@0 191 state.pluginExtension = true;
michael@0 192 state.noChannel = false;
michael@0 193 },
michael@0 194 document: function(obj, state) {
michael@0 195 removeAttr(obj, "type");
michael@0 196 src(obj, state, "data:text/plain,I am a document");
michael@0 197 state.noChannel = false;
michael@0 198 state.expectedType = "text/plain";
michael@0 199 state.expectedMode = "document";
michael@0 200 },
michael@0 201 fallback: function(obj, state) {
michael@0 202 removeAttr(obj, "type");
michael@0 203 state.expectedType = "application/x-unknown";
michael@0 204 state.expectedMode = "none";
michael@0 205 state.noChannel = true;
michael@0 206 src(obj, state, null);
michael@0 207 },
michael@0 208 addToDoc: function(obj, state) {
michael@0 209 body.appendChild(obj);
michael@0 210 },
michael@0 211 removeFromDoc: function(obj, state) {
michael@0 212 if (body.contains(obj))
michael@0 213 body.removeChild(obj);
michael@0 214 },
michael@0 215 // Set the proper type
michael@0 216 setType: function(obj, state) {
michael@0 217 if (state.expectedType) {
michael@0 218 state.badType = false;
michael@0 219 setAttr(obj, 'type', state.expectedType);
michael@0 220 forceReload(obj, state);
michael@0 221 }
michael@0 222 },
michael@0 223 // Set an improper type
michael@0 224 setWrongType: function(obj, state) {
michael@0 225 // This should break no-channel-plugins but nothing else
michael@0 226 state.badType = true;
michael@0 227 setAttr(obj, 'type', "application/x-unknown");
michael@0 228 forceReload(obj, state);
michael@0 229 },
michael@0 230 // Set a plugin type
michael@0 231 setPluginType: function(obj, state) {
michael@0 232 // If an object/embed has a type set to a plugin type, it should not
michael@0 233 // use the channel type.
michael@0 234 state.badType = false;
michael@0 235 setAttr(obj, 'type', 'application/x-test');
michael@0 236 state.expectedType = "application/x-test";
michael@0 237 state.expectedMode = "plugin";
michael@0 238 forceReload(obj, state);
michael@0 239 },
michael@0 240 noChannel: function(obj, state) {
michael@0 241 src(obj, state, null);
michael@0 242 state.noChannel = true;
michael@0 243 state.pluginExtension = false;
michael@0 244 },
michael@0 245 displayNone: function(obj, state) {
michael@0 246 setDisplayed(obj, false);
michael@0 247 },
michael@0 248 displayInherit: function(obj, state) {
michael@0 249 setDisplayed(obj, true);
michael@0 250 }
michael@0 251 };
michael@0 252
michael@0 253
michael@0 254 function testObject(obj, state) {
michael@0 255 // If our test combination both sets noChannel but no explicit type
michael@0 256 // it shouldn't load ever.
michael@0 257 let expectedMode = state.expectedMode;
michael@0 258 let actualMode = getMode(obj, state);
michael@0 259
michael@0 260 if (state.noChannel && !getAttr(obj, 'type')) {
michael@0 261 // Some combinations of test both set no type and no channel. This is
michael@0 262 // worth testing with the various combinations, but shouldn't load.
michael@0 263 expectedMode = "none";
michael@0 264 }
michael@0 265
michael@0 266 // Embed tags should always try to load a plugin by type or extension
michael@0 267 // before falling back to opening a channel. See bug 803159
michael@0 268 if (state.tagName == "embed" &&
michael@0 269 (getAttr(obj, 'type') == "application/x-test" || state.pluginExtension)) {
michael@0 270 state.noChannel = true;
michael@0 271 }
michael@0 272
michael@0 273 // with state.loading, unless we're loading with no channel, these types
michael@0 274 // should still be in loading state pending a channel.
michael@0 275 if (state.loading && (expectedMode == "image" || expectedMode == "document" ||
michael@0 276 (expectedMode == "plugin" && !state.initialPlugin && !state.noChannel))) {
michael@0 277 expectedMode = "loading";
michael@0 278 }
michael@0 279
michael@0 280 // With the exception of plugins with a proper type, nothing should
michael@0 281 // load without a channel
michael@0 282 if (state.noChannel && (expectedMode != "plugin" || state.badType) &&
michael@0 283 body.contains(obj)) {
michael@0 284 expectedMode = "none";
michael@0 285 }
michael@0 286
michael@0 287 // embed tags should reject documents, except for SVG images which
michael@0 288 // render as such
michael@0 289 if (state.tagName == "embed" && expectedMode == "document" &&
michael@0 290 actualType(obj, state) != "image/svg+xml") {
michael@0 291 expectedMode = "none";
michael@0 292 }
michael@0 293
michael@0 294 // Embeds with a plugin type should skip opening a channel prior to
michael@0 295 // loading, taking only type into account.
michael@0 296 if (state.tagName == 'embed' && getAttr(obj, 'type') == 'application/x-test' &&
michael@0 297 body.contains(obj)) {
michael@0 298 expectedMode = "plugin";
michael@0 299 }
michael@0 300
michael@0 301 if (!body.contains(obj)
michael@0 302 && (!state.loading || expectedMode != "image")
michael@0 303 && (!state.initialPlugin || expectedMode != "plugin")) {
michael@0 304 // Images are handled by nsIImageLoadingContent so we dont track
michael@0 305 // their state change as they're detached and reattached. All other
michael@0 306 // types switch to state "loading", and are completely unloaded
michael@0 307 expectedMode = "loading";
michael@0 308 }
michael@0 309
michael@0 310 is(actualMode, expectedMode, "check loaded mode");
michael@0 311
michael@0 312 // If we're a plugin, check that we spawned successfully. state.loading
michael@0 313 // is set if we haven't had an event loop since applying state, in which
michael@0 314 // case the plugin would not have stopped yet if it was initially a
michael@0 315 // plugin.
michael@0 316 let shouldBeSpawnable = expectedMode == "plugin" && displayed(obj);
michael@0 317 let shouldSpawn = shouldBeSpawnable && (!state.loading || state.initialPlugin);
michael@0 318 let didSpawn = runningPlugin(obj, state);
michael@0 319 is(didSpawn, !!shouldSpawn, "check plugin spawned is " + !!shouldSpawn);
michael@0 320
michael@0 321 // If we are a plugin, scripting should work. If we're not spawned we
michael@0 322 // should spawn synchronously.
michael@0 323 let scripted = false;
michael@0 324 try {
michael@0 325 let x = obj.getObjectValue();
michael@0 326 scripted = true;
michael@0 327 } catch(e) {}
michael@0 328 is(scripted, shouldBeSpawnable, "check plugin scriptability");
michael@0 329
michael@0 330 // If this tag previously had other spawned plugins, make sure it
michael@0 331 // respawned between then and now
michael@0 332 if (state.oldPlugins && didSpawn) {
michael@0 333 let didRespawn = false;
michael@0 334 for (let oldp of state.oldPlugins) {
michael@0 335 // If this returns false or throws, it's not the same plugin
michael@0 336 try {
michael@0 337 didRespawn = !obj.checkObjectValue(oldp);
michael@0 338 } catch (e) {
michael@0 339 didRespawn = true;
michael@0 340 }
michael@0 341 }
michael@0 342 is(didRespawn, true, "Plugin should have re-spawned since old state ("+state.oldPlugins.length+")");
michael@0 343 }
michael@0 344 }
michael@0 345
michael@0 346 let total = 0;
michael@0 347 let test_modes = {
michael@0 348 // Just apply from_state then to_state
michael@0 349 "immediate": function(obj, from_state, to_state, state) {
michael@0 350 for (let from of from_state)
michael@0 351 states[from](obj, state);
michael@0 352 for (let to of to_state)
michael@0 353 states[to](obj, state);
michael@0 354
michael@0 355 // We don't spin the event loop between applying to_state and
michael@0 356 // running tests, so some types are still loading
michael@0 357 state.loading = true;
michael@0 358 info("["+(++total)+"] Testing [ " + from_state + " ] -> [ " + to_state + " ] / " + state.tagName + " / immediate");
michael@0 359 testObject(obj, state);
michael@0 360
michael@0 361 if (body.contains(obj))
michael@0 362 body.removeChild(obj);
michael@0 363
michael@0 364 },
michael@0 365 // Apply states, spin event loop, run tests.
michael@0 366 "cycle": function(obj, from_state, to_state, state) {
michael@0 367 for (let from of from_state)
michael@0 368 states[from](obj, state);
michael@0 369 for (let to of to_state)
michael@0 370 states[to](obj, state);
michael@0 371 // Because re-appending to the document creates a script blocker, but
michael@0 372 // plugins spawn asynchronously, we need to return to the event loop
michael@0 373 // twice to ensure the plugin has been given a chance to lazily spawn.
michael@0 374 runSoon(function() { runSoon(function() {
michael@0 375 info("["+(++total)+"] Testing [ " + from_state + " ] -> [ " + to_state + " ] / " + state.tagName + " / cycle");
michael@0 376 testObject(obj, state);
michael@0 377
michael@0 378 if (body.contains(obj))
michael@0 379 body.removeChild(obj);
michael@0 380 }); });
michael@0 381 },
michael@0 382 // Apply initial state, spin event loop, apply final state, spin event
michael@0 383 // loop again.
michael@0 384 "cycleboth": function(obj, from_state, to_state, state) {
michael@0 385 for (let from of from_state) {
michael@0 386 states[from](obj, state);
michael@0 387 }
michael@0 388 runSoon(function() {
michael@0 389 for (let to of to_state) {
michael@0 390 states[to](obj, state);
michael@0 391 }
michael@0 392 // Because re-appending to the document creates a script blocker,
michael@0 393 // but plugins spawn asynchronously, we need to return to the event
michael@0 394 // loop twice to ensure the plugin has been given a chance to lazily
michael@0 395 // spawn.
michael@0 396 runSoon(function() { runSoon(function() {
michael@0 397 info("["+(++total)+"] Testing [ " + from_state + " ] -> [ " + to_state + " ] / " + state.tagName + " / cycleboth");
michael@0 398 testObject(obj, state);
michael@0 399
michael@0 400 if (body.contains(obj))
michael@0 401 body.removeChild(obj);
michael@0 402 }); });
michael@0 403 });
michael@0 404 },
michael@0 405 // Apply initial state, spin event loop, apply later state, test
michael@0 406 // immediately
michael@0 407 "cyclefirst": function(obj, from_state, to_state, state) {
michael@0 408 for (let from of from_state) {
michael@0 409 states[from](obj, state);
michael@0 410 }
michael@0 411 runSoon(function() {
michael@0 412 state.initialPlugin = runningPlugin(obj, state);
michael@0 413 for (let to of to_state) {
michael@0 414 states[to](obj, state);
michael@0 415 }
michael@0 416 info("["+(++total)+"] Testing [ " + from_state + " ] -> [ " + to_state + " ] / " + state.tagName + " / cyclefirst");
michael@0 417 // We don't spin the event loop between applying to_state and
michael@0 418 // running tests, so some types are still loading
michael@0 419 state.loading = true;
michael@0 420 testObject(obj, state);
michael@0 421
michael@0 422 if (body.contains(obj))
michael@0 423 body.removeChild(obj);
michael@0 424 });
michael@0 425 },
michael@0 426 };
michael@0 427
michael@0 428 function test(testdat) {
michael@0 429 for (let from_state of testdat['from_states']) {
michael@0 430 for (let to_state of testdat['to_states']) {
michael@0 431 for (let mode of testdat['test_modes']) {
michael@0 432 for (let type of testdat['tag_types']) {
michael@0 433 runSoon(function () {
michael@0 434 let obj = document.createElement(type);
michael@0 435 obj.width = 1; obj.height = 1;
michael@0 436 let state = {};
michael@0 437 state.noChannel = true;
michael@0 438 state.tagName = type;
michael@0 439 // Part of the test checks whether a plugin spawned or not,
michael@0 440 // but touching the object prototype will attempt to
michael@0 441 // synchronously spawn a plugin! We use this terrible hack to
michael@0 442 // get a privileged getter for the attributes we want to touch
michael@0 443 // prior to applying any attributes.
michael@0 444 // TODO when embed goes away we wont need to check for
michael@0 445 // QueryInterface any longer.
michael@0 446 var lookup_on = obj.QueryInterface ? obj.QueryInterface(OBJLC): obj;
michael@0 447 state.getDisplayedType = SpecialPowers.do_lookupGetter(lookup_on, 'displayedType');
michael@0 448 state.getHasRunningPlugin = SpecialPowers.do_lookupGetter(lookup_on, 'hasRunningPlugin');
michael@0 449 state.getActualType = SpecialPowers.do_lookupGetter(lookup_on, 'actualType');
michael@0 450 test_modes[mode](obj, from_state, to_state, state);
michael@0 451 });
michael@0 452 }
michael@0 453 }
michael@0 454 }
michael@0 455 }
michael@0 456 }
michael@0 457
michael@0 458 function onLoad() {
michael@0 459 // Generic tests
michael@0 460 test({
michael@0 461 'tag_types': [ 'embed', 'object' ],
michael@0 462 // In all three modes
michael@0 463 'test_modes': [ 'immediate', 'cycle', 'cyclefirst', 'cycleboth' ],
michael@0 464 // Starting from a blank tag in and out of the document, a loading
michael@0 465 // plugin, and no-channel plugin (initial types only really have
michael@0 466 // odd cases with plugins)
michael@0 467 'from_states': [
michael@0 468 [ 'addToDoc' ],
michael@0 469 [ 'plugin' ],
michael@0 470 [ 'plugin', 'addToDoc' ],
michael@0 471 [ 'plugin', 'noChannel', 'setType', 'addToDoc' ],
michael@0 472 [],
michael@0 473 ],
michael@0 474 // To various combinations of loaded objects
michael@0 475 'to_states': eachList(
michael@0 476 [ 'svg', 'image', 'plugin', 'document', '' ],
michael@0 477 [ 'setType', 'setWrongType', 'setPluginType', '' ],
michael@0 478 [ 'noChannel', '' ],
michael@0 479 [ 'displayNone', 'displayInherit', '' ]
michael@0 480 )});
michael@0 481 // Special case test for embed tags with plugin-by-extension
michael@0 482 // TODO object tags should be tested here too -- they have slightly
michael@0 483 // different behavior, but waiting on a file load requires a loaded
michael@0 484 // event handler and wont work with just our event loop spinning.
michael@0 485 test({
michael@0 486 'tag_types': [ 'embed' ],
michael@0 487 'test_modes': [ 'immediate', 'cyclefirst', 'cycle', 'cycleboth' ],
michael@0 488 'from_states': eachList(
michael@0 489 [ 'svg', 'plugin', 'image', 'document' ],
michael@0 490 [ 'addToDoc' ]
michael@0 491 ),
michael@0 492 // Set extension along with valid ty
michael@0 493 'to_states': [
michael@0 494 [ 'pluginExtension' ]
michael@0 495 ]});
michael@0 496 // Test plugin add/remove from document with adding/removing frame, with
michael@0 497 // and without a channel.
michael@0 498 test({
michael@0 499 'tag_types': [ 'embed', 'object' ], // Ideally we'd test object too, but this gets exponentially long.
michael@0 500 'test_modes': [ 'immediate', 'cyclefirst', 'cycle' ],
michael@0 501 'from_states': [ [ 'displayNone', 'plugin', 'addToDoc' ],
michael@0 502 [ 'displayNone', 'plugin', 'noChannel', 'addToDoc' ],
michael@0 503 [ 'plugin', 'noChannel', 'addToDoc' ],
michael@0 504 [ 'plugin', 'noChannel' ] ],
michael@0 505 'to_states': eachList(
michael@0 506 [ 'displayNone', '' ],
michael@0 507 [ 'removeFromDoc' ],
michael@0 508 [ 'image', 'displayNone', '' ],
michael@0 509 [ 'image', 'displayNone', '' ],
michael@0 510 [ 'addToDoc' ],
michael@0 511 [ 'displayInherit' ]
michael@0 512 )});
michael@0 513 runWhenDone(function() SimpleTest.finish());
michael@0 514 }
michael@0 515 </script>

mercurial