addon-sdk/source/lib/sdk/tabs/tab-firefox.js

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:b7c27dc64ffb
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 'use strict';
5
6 const { Trait } = require("../deprecated/traits");
7 const { EventEmitter } = require("../deprecated/events");
8 const { defer } = require("../lang/functional");
9 const { has } = require("../util/array");
10 const { EVENTS } = require("./events");
11 const { getThumbnailURIForWindow } = require("../content/thumbnail");
12 const { getFaviconURIForLocation } = require("../io/data");
13 const { activateTab, getOwnerWindow, getBrowserForTab, getTabTitle, setTabTitle,
14 getTabURL, setTabURL, getTabContentType, getTabId } = require('./utils');
15 const { getOwnerWindow: getPBOwnerWindow } = require('../private-browsing/window/utils');
16 const viewNS = require('../core/namespace').ns();
17 const { deprecateUsage } = require('../util/deprecate');
18 const { getURL } = require('../url/utils');
19 const { viewFor } = require('../view/core');
20
21 // Array of the inner instances of all the wrapped tabs.
22 const TABS = [];
23
24 /**
25 * Trait used to create tab wrappers.
26 */
27 const TabTrait = Trait.compose(EventEmitter, {
28 on: Trait.required,
29 _emit: Trait.required,
30 /**
31 * Tab DOM element that is being wrapped.
32 */
33 _tab: null,
34 /**
35 * Window wrapper whose tab this object represents.
36 */
37 window: null,
38 constructor: function Tab(options) {
39 this._onReady = this._onReady.bind(this);
40 this._onLoad = this._onLoad.bind(this);
41 this._onPageShow = this._onPageShow.bind(this);
42 this._tab = options.tab;
43 // TODO: Remove this dependency
44 let window = this.window = options.window || require('../windows').BrowserWindow({ window: getOwnerWindow(this._tab) });
45
46 // Setting event listener if was passed.
47 for each (let type in EVENTS) {
48 let listener = options[type.listener];
49 if (listener) {
50 this.on(type.name, options[type.listener]);
51 }
52 // window spreads this event.
53 if (!has(['ready', 'load', 'pageshow'], (type.name)))
54 window.tabs.on(type.name, this._onEvent.bind(this, type.name));
55 }
56
57 this.on(EVENTS.close.name, this.destroy.bind(this));
58
59 this._browser.addEventListener(EVENTS.ready.dom, this._onReady, true);
60 this._browser.addEventListener(EVENTS.load.dom, this._onLoad, true);
61 this._browser.addEventListener(EVENTS.pageshow.dom, this._onPageShow, true);
62
63 if (options.isPinned)
64 this.pin();
65
66 viewNS(this._public).tab = this._tab;
67 getPBOwnerWindow.implement(this._public, getChromeTab);
68 viewFor.implement(this._public, getTabView);
69
70 // Add tabs to getURL method
71 getURL.implement(this._public, (function (obj) this._public.url).bind(this));
72
73 // Since we will have to identify tabs by a DOM elements facade function
74 // is used as constructor that collects all the instances and makes sure
75 // that they more then one wrapper is not created per tab.
76 return this;
77 },
78 destroy: function destroy() {
79 this._removeAllListeners();
80 if (this._tab) {
81 let browser = this._browser;
82 // The tab may already be removed from DOM -or- not yet added
83 if (browser) {
84 browser.removeEventListener(EVENTS.ready.dom, this._onReady, true);
85 browser.removeEventListener(EVENTS.load.dom, this._onLoad, true);
86 browser.removeEventListener(EVENTS.pageshow.dom, this._onPageShow, true);
87 }
88 this._tab = null;
89 TABS.splice(TABS.indexOf(this), 1);
90 }
91 },
92
93 /**
94 * Internal listener that emits public event 'ready' when the page of this
95 * tab is loaded, from DOMContentLoaded
96 */
97 _onReady: function _onReady(event) {
98 // IFrames events will bubble so we need to ignore those.
99 if (event.target == this._contentDocument)
100 this._emit(EVENTS.ready.name, this._public);
101 },
102
103 /**
104 * Internal listener that emits public event 'load' when the page of this
105 * tab is loaded, for triggering on non-HTML content, bug #671305
106 */
107 _onLoad: function _onLoad(event) {
108 // IFrames events will bubble so we need to ignore those.
109 if (event.target == this._contentDocument) {
110 this._emit(EVENTS.load.name, this._public);
111 }
112 },
113
114 /**
115 * Internal listener that emits public event 'pageshow' when the page of this
116 * tab is loaded from cache, bug #671305
117 */
118 _onPageShow: function _onPageShow(event) {
119 // IFrames events will bubble so we need to ignore those.
120 if (event.target == this._contentDocument) {
121 this._emit(EVENTS.pageshow.name, this._public, event.persisted);
122 }
123 },
124 /**
125 * Internal tab event router. Window will emit tab related events for all it's
126 * tabs, this listener will propagate all the events for this tab to it's
127 * listeners.
128 */
129 _onEvent: function _onEvent(type, tab) {
130 if (viewNS(tab).tab == this._tab)
131 this._emit(type, tab);
132 },
133 /**
134 * Browser DOM element where page of this tab is currently loaded.
135 */
136 get _browser() getBrowserForTab(this._tab),
137 /**
138 * Window DOM element containing this tab.
139 */
140 get _window() getOwnerWindow(this._tab),
141 /**
142 * Document object of the page that is currently loaded in this tab.
143 */
144 get _contentDocument() this._browser.contentDocument,
145 /**
146 * Window object of the page that is currently loaded in this tab.
147 */
148 get _contentWindow() this._browser.contentWindow,
149
150 /**
151 * Unique id for the tab, actually maps to tab.linkedPanel but with some munging.
152 */
153 get id() this._tab ? getTabId(this._tab) : undefined,
154
155 /**
156 * The title of the page currently loaded in the tab.
157 * Changing this property changes an actual title.
158 * @type {String}
159 */
160 get title() this._tab ? getTabTitle(this._tab) : undefined,
161 set title(title) this._tab && setTabTitle(this._tab, title),
162
163 /**
164 * Returns the MIME type that the document loaded in the tab is being
165 * rendered as.
166 * @type {String}
167 */
168 get contentType() this._tab ? getTabContentType(this._tab) : undefined,
169
170 /**
171 * Location of the page currently loaded in this tab.
172 * Changing this property will loads page under under the specified location.
173 * @type {String}
174 */
175 get url() this._tab ? getTabURL(this._tab) : undefined,
176 set url(url) this._tab && setTabURL(this._tab, url),
177 /**
178 * URI of the favicon for the page currently loaded in this tab.
179 * @type {String}
180 */
181 get favicon() {
182 deprecateUsage(
183 'tab.favicon is deprecated, ' +
184 'please use require("sdk/places/favicon").getFavicon instead.'
185 );
186 return this._tab ? getFaviconURIForLocation(this.url) : undefined
187 },
188 /**
189 * The CSS style for the tab
190 */
191 get style() null, // TODO
192 /**
193 * The index of the tab relative to other tabs in the application window.
194 * Changing this property will change order of the actual position of the tab.
195 * @type {Number}
196 */
197 get index()
198 this._tab ?
199 this._window.gBrowser.getBrowserIndexForDocument(this._contentDocument) :
200 undefined,
201 set index(value)
202 this._tab && this._window.gBrowser.moveTabTo(this._tab, value),
203 /**
204 * Thumbnail data URI of the page currently loaded in this tab.
205 * @type {String}
206 */
207 getThumbnail: function getThumbnail()
208 this._tab ? getThumbnailURIForWindow(this._contentWindow) : undefined,
209 /**
210 * Whether or not tab is pinned (Is an app-tab).
211 * @type {Boolean}
212 */
213 get isPinned() this._tab ? this._tab.pinned : undefined,
214 pin: function pin() {
215 if (!this._tab)
216 return;
217 this._window.gBrowser.pinTab(this._tab);
218 },
219 unpin: function unpin() {
220 if (!this._tab)
221 return;
222 this._window.gBrowser.unpinTab(this._tab);
223 },
224
225 /**
226 * Create a worker for this tab, first argument is options given to Worker.
227 * @type {Worker}
228 */
229 attach: function attach(options) {
230 if (!this._tab)
231 return;
232 // BUG 792946 https://bugzilla.mozilla.org/show_bug.cgi?id=792946
233 // TODO: fix this circular dependency
234 let { Worker } = require('./worker');
235 return Worker(options, this._contentWindow);
236 },
237
238 /**
239 * Make this tab active.
240 * Please note: That this function is called asynchronous since in E10S that
241 * will be the case. Besides this function is called from a constructor where
242 * we would like to return instance before firing a 'TabActivated' event.
243 */
244 activate: defer(function activate() {
245 if (!this._tab)
246 return;
247 activateTab(this._tab);
248 }),
249 /**
250 * Close the tab
251 */
252 close: function close(callback) {
253 // Bug 699450: the tab may already have been detached
254 if (!this._tab || !this._tab.parentNode) {
255 if (callback)
256 callback();
257 return;
258 }
259 if (callback)
260 this.once(EVENTS.close.name, callback);
261 this._window.gBrowser.removeTab(this._tab);
262 },
263 /**
264 * Reload the tab
265 */
266 reload: function reload() {
267 if (!this._tab)
268 return;
269 this._window.gBrowser.reloadTab(this._tab);
270 }
271 });
272
273 function getChromeTab(tab) {
274 return getOwnerWindow(viewNS(tab).tab);
275 }
276
277 // Implement `viewFor` polymorphic function for the Tab
278 // instances.
279 const getTabView = tab => viewNS(tab).tab;
280
281 function Tab(options, existingOnly) {
282 let chromeTab = options.tab;
283 for each (let tab in TABS) {
284 if (chromeTab == tab._tab)
285 return tab._public;
286 }
287 // If called asked to return only existing wrapper,
288 // we should return null here as no matching Tab object has been found
289 if (existingOnly)
290 return null;
291
292 let tab = TabTrait(options);
293 TABS.push(tab);
294 return tab._public;
295 }
296 Tab.prototype = TabTrait.prototype;
297 exports.Tab = Tab;

mercurial