1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/lib/sdk/frame/hidden-frame.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,116 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +module.metadata = { 1.11 + "stability": "experimental" 1.12 +}; 1.13 + 1.14 +const { Cc, Ci } = require("chrome"); 1.15 +const errors = require("../deprecated/errors"); 1.16 +const { Class } = require("../core/heritage"); 1.17 +const { List, addListItem, removeListItem } = require("../util/list"); 1.18 +const { EventTarget } = require("../event/target"); 1.19 +const { emit } = require("../event/core"); 1.20 +const { create: makeFrame } = require("./utils"); 1.21 +const { defer } = require("../core/promise"); 1.22 +const { when: unload } = require("../system/unload"); 1.23 +const { validateOptions, getTypeOf } = require("../deprecated/api-utils"); 1.24 +const { window } = require("../addon/window"); 1.25 +const { fromIterator } = require("../util/array"); 1.26 + 1.27 +// This cache is used to access friend properties between functions 1.28 +// without exposing them on the public API. 1.29 +let cache = new Set(); 1.30 +let elements = new WeakMap(); 1.31 + 1.32 +function contentLoaded(target) { 1.33 + var deferred = defer(); 1.34 + target.addEventListener("DOMContentLoaded", function DOMContentLoaded(event) { 1.35 + // "DOMContentLoaded" events from nested frames propagate up to target, 1.36 + // ignore events unless it's DOMContentLoaded for the given target. 1.37 + if (event.target === target || event.target === target.contentDocument) { 1.38 + target.removeEventListener("DOMContentLoaded", DOMContentLoaded, false); 1.39 + deferred.resolve(target); 1.40 + } 1.41 + }, false); 1.42 + return deferred.promise; 1.43 +} 1.44 + 1.45 +function FrameOptions(options) { 1.46 + options = options || {} 1.47 + return validateOptions(options, FrameOptions.validator); 1.48 +} 1.49 +FrameOptions.validator = { 1.50 + onReady: { 1.51 + is: ["undefined", "function", "array"], 1.52 + ok: function(v) { 1.53 + if (getTypeOf(v) === "array") { 1.54 + // make sure every item is a function 1.55 + return v.every(function (item) typeof(item) === "function") 1.56 + } 1.57 + return true; 1.58 + } 1.59 + }, 1.60 + onUnload: { 1.61 + is: ["undefined", "function"] 1.62 + } 1.63 +}; 1.64 + 1.65 +var HiddenFrame = Class({ 1.66 + extends: EventTarget, 1.67 + initialize: function initialize(options) { 1.68 + options = FrameOptions(options); 1.69 + EventTarget.prototype.initialize.call(this, options); 1.70 + }, 1.71 + get element() { 1.72 + return elements.get(this); 1.73 + }, 1.74 + toString: function toString() { 1.75 + return "[object Frame]" 1.76 + } 1.77 +}); 1.78 +exports.HiddenFrame = HiddenFrame 1.79 + 1.80 +function addHidenFrame(frame) { 1.81 + if (!(frame instanceof HiddenFrame)) 1.82 + throw Error("The object to be added must be a HiddenFrame."); 1.83 + 1.84 + // This instance was already added. 1.85 + if (cache.has(frame)) return frame; 1.86 + else cache.add(frame); 1.87 + 1.88 + let element = makeFrame(window.document, { 1.89 + nodeName: "iframe", 1.90 + type: "content", 1.91 + allowJavascript: true, 1.92 + allowPlugins: true, 1.93 + allowAuth: true, 1.94 + }); 1.95 + elements.set(frame, element); 1.96 + 1.97 + contentLoaded(element).then(function onFrameReady(element) { 1.98 + emit(frame, "ready"); 1.99 + }, console.exception); 1.100 + 1.101 + return frame; 1.102 +} 1.103 +exports.add = addHidenFrame 1.104 + 1.105 +function removeHiddenFrame(frame) { 1.106 + if (!(frame instanceof HiddenFrame)) 1.107 + throw Error("The object to be removed must be a HiddenFrame."); 1.108 + 1.109 + if (!cache.has(frame)) return; 1.110 + 1.111 + // Remove from cache before calling in order to avoid loop 1.112 + cache.delete(frame); 1.113 + emit(frame, "unload") 1.114 + let element = frame.element 1.115 + if (element) element.parentNode.removeChild(element) 1.116 +} 1.117 +exports.remove = removeHiddenFrame; 1.118 + 1.119 +unload(function() fromIterator(cache).forEach(removeHiddenFrame));