|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 module.metadata = { |
|
8 "stability": "experimental" |
|
9 }; |
|
10 |
|
11 const { Cc, Ci } = require("chrome"); |
|
12 const errors = require("../deprecated/errors"); |
|
13 const { Class } = require("../core/heritage"); |
|
14 const { List, addListItem, removeListItem } = require("../util/list"); |
|
15 const { EventTarget } = require("../event/target"); |
|
16 const { emit } = require("../event/core"); |
|
17 const { create: makeFrame } = require("./utils"); |
|
18 const { defer } = require("../core/promise"); |
|
19 const { when: unload } = require("../system/unload"); |
|
20 const { validateOptions, getTypeOf } = require("../deprecated/api-utils"); |
|
21 const { window } = require("../addon/window"); |
|
22 const { fromIterator } = require("../util/array"); |
|
23 |
|
24 // This cache is used to access friend properties between functions |
|
25 // without exposing them on the public API. |
|
26 let cache = new Set(); |
|
27 let elements = new WeakMap(); |
|
28 |
|
29 function contentLoaded(target) { |
|
30 var deferred = defer(); |
|
31 target.addEventListener("DOMContentLoaded", function DOMContentLoaded(event) { |
|
32 // "DOMContentLoaded" events from nested frames propagate up to target, |
|
33 // ignore events unless it's DOMContentLoaded for the given target. |
|
34 if (event.target === target || event.target === target.contentDocument) { |
|
35 target.removeEventListener("DOMContentLoaded", DOMContentLoaded, false); |
|
36 deferred.resolve(target); |
|
37 } |
|
38 }, false); |
|
39 return deferred.promise; |
|
40 } |
|
41 |
|
42 function FrameOptions(options) { |
|
43 options = options || {} |
|
44 return validateOptions(options, FrameOptions.validator); |
|
45 } |
|
46 FrameOptions.validator = { |
|
47 onReady: { |
|
48 is: ["undefined", "function", "array"], |
|
49 ok: function(v) { |
|
50 if (getTypeOf(v) === "array") { |
|
51 // make sure every item is a function |
|
52 return v.every(function (item) typeof(item) === "function") |
|
53 } |
|
54 return true; |
|
55 } |
|
56 }, |
|
57 onUnload: { |
|
58 is: ["undefined", "function"] |
|
59 } |
|
60 }; |
|
61 |
|
62 var HiddenFrame = Class({ |
|
63 extends: EventTarget, |
|
64 initialize: function initialize(options) { |
|
65 options = FrameOptions(options); |
|
66 EventTarget.prototype.initialize.call(this, options); |
|
67 }, |
|
68 get element() { |
|
69 return elements.get(this); |
|
70 }, |
|
71 toString: function toString() { |
|
72 return "[object Frame]" |
|
73 } |
|
74 }); |
|
75 exports.HiddenFrame = HiddenFrame |
|
76 |
|
77 function addHidenFrame(frame) { |
|
78 if (!(frame instanceof HiddenFrame)) |
|
79 throw Error("The object to be added must be a HiddenFrame."); |
|
80 |
|
81 // This instance was already added. |
|
82 if (cache.has(frame)) return frame; |
|
83 else cache.add(frame); |
|
84 |
|
85 let element = makeFrame(window.document, { |
|
86 nodeName: "iframe", |
|
87 type: "content", |
|
88 allowJavascript: true, |
|
89 allowPlugins: true, |
|
90 allowAuth: true, |
|
91 }); |
|
92 elements.set(frame, element); |
|
93 |
|
94 contentLoaded(element).then(function onFrameReady(element) { |
|
95 emit(frame, "ready"); |
|
96 }, console.exception); |
|
97 |
|
98 return frame; |
|
99 } |
|
100 exports.add = addHidenFrame |
|
101 |
|
102 function removeHiddenFrame(frame) { |
|
103 if (!(frame instanceof HiddenFrame)) |
|
104 throw Error("The object to be removed must be a HiddenFrame."); |
|
105 |
|
106 if (!cache.has(frame)) return; |
|
107 |
|
108 // Remove from cache before calling in order to avoid loop |
|
109 cache.delete(frame); |
|
110 emit(frame, "unload") |
|
111 let element = frame.element |
|
112 if (element) element.parentNode.removeChild(element) |
|
113 } |
|
114 exports.remove = removeHiddenFrame; |
|
115 |
|
116 unload(function() fromIterator(cache).forEach(removeHiddenFrame)); |