addon-sdk/source/lib/sdk/ui/sidebar.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4 'use strict';
michael@0 5
michael@0 6 module.metadata = {
michael@0 7 'stability': 'experimental',
michael@0 8 'engines': {
michael@0 9 'Firefox': '*'
michael@0 10 }
michael@0 11 };
michael@0 12
michael@0 13 const { Class } = require('../core/heritage');
michael@0 14 const { merge } = require('../util/object');
michael@0 15 const { Disposable } = require('../core/disposable');
michael@0 16 const { off, emit, setListeners } = require('../event/core');
michael@0 17 const { EventTarget } = require('../event/target');
michael@0 18 const { URL } = require('../url');
michael@0 19 const { add, remove, has, clear, iterator } = require('../lang/weak-set');
michael@0 20 const { id: addonID } = require('../self');
michael@0 21 const { WindowTracker } = require('../deprecated/window-utils');
michael@0 22 const { isShowing } = require('./sidebar/utils');
michael@0 23 const { isBrowser, getMostRecentBrowserWindow, windows, isWindowPrivate } = require('../window/utils');
michael@0 24 const { ns } = require('../core/namespace');
michael@0 25 const { remove: removeFromArray } = require('../util/array');
michael@0 26 const { show, hide, toggle } = require('./sidebar/actions');
michael@0 27 const { Worker } = require('../content/worker');
michael@0 28 const { contract: sidebarContract } = require('./sidebar/contract');
michael@0 29 const { create, dispose, updateTitle, updateURL, isSidebarShowing, showSidebar, hideSidebar } = require('./sidebar/view');
michael@0 30 const { defer } = require('../core/promise');
michael@0 31 const { models, views, viewsFor, modelFor } = require('./sidebar/namespace');
michael@0 32 const { isLocalURL } = require('../url');
michael@0 33 const { ensure } = require('../system/unload');
michael@0 34 const { identify } = require('./id');
michael@0 35 const { uuid } = require('../util/uuid');
michael@0 36
michael@0 37 const sidebarNS = ns();
michael@0 38
michael@0 39 const WEB_PANEL_BROWSER_ID = 'web-panels-browser';
michael@0 40
michael@0 41 let sidebars = {};
michael@0 42
michael@0 43 const Sidebar = Class({
michael@0 44 implements: [ Disposable ],
michael@0 45 extends: EventTarget,
michael@0 46 setup: function(options) {
michael@0 47 // inital validation for the model information
michael@0 48 let model = sidebarContract(options);
michael@0 49
michael@0 50 // save the model information
michael@0 51 models.set(this, model);
michael@0 52
michael@0 53 // generate an id if one was not provided
michael@0 54 model.id = model.id || addonID + '-' + uuid();
michael@0 55
michael@0 56 // further validation for the title and url
michael@0 57 validateTitleAndURLCombo({}, this.title, this.url);
michael@0 58
michael@0 59 const self = this;
michael@0 60 const internals = sidebarNS(self);
michael@0 61 const windowNS = internals.windowNS = ns();
michael@0 62
michael@0 63 // see bug https://bugzilla.mozilla.org/show_bug.cgi?id=886148
michael@0 64 ensure(this, 'destroy');
michael@0 65
michael@0 66 setListeners(this, options);
michael@0 67
michael@0 68 let bars = [];
michael@0 69 internals.tracker = WindowTracker({
michael@0 70 onTrack: function(window) {
michael@0 71 if (!isBrowser(window))
michael@0 72 return;
michael@0 73
michael@0 74 let sidebar = window.document.getElementById('sidebar');
michael@0 75 let sidebarBox = window.document.getElementById('sidebar-box');
michael@0 76
michael@0 77 let bar = create(window, {
michael@0 78 id: self.id,
michael@0 79 title: self.title,
michael@0 80 sidebarurl: self.url
michael@0 81 });
michael@0 82 bars.push(bar);
michael@0 83 windowNS(window).bar = bar;
michael@0 84
michael@0 85 bar.addEventListener('command', function() {
michael@0 86 if (isSidebarShowing(window, self)) {
michael@0 87 hideSidebar(window, self);
michael@0 88 return;
michael@0 89 }
michael@0 90
michael@0 91 showSidebar(window, self);
michael@0 92 }, false);
michael@0 93
michael@0 94 function onSidebarLoad() {
michael@0 95 // check if the sidebar is ready
michael@0 96 let isReady = sidebar.docShell && sidebar.contentDocument;
michael@0 97 if (!isReady)
michael@0 98 return;
michael@0 99
michael@0 100 // check if it is a web panel
michael@0 101 let panelBrowser = sidebar.contentDocument.getElementById(WEB_PANEL_BROWSER_ID);
michael@0 102 if (!panelBrowser) {
michael@0 103 bar.removeAttribute('checked');
michael@0 104 return;
michael@0 105 }
michael@0 106
michael@0 107 let sbTitle = window.document.getElementById('sidebar-title');
michael@0 108 function onWebPanelSidebarCreated() {
michael@0 109 if (panelBrowser.contentWindow.location != model.url ||
michael@0 110 sbTitle.value != model.title) {
michael@0 111 return;
michael@0 112 }
michael@0 113
michael@0 114 let worker = windowNS(window).worker = Worker({
michael@0 115 window: panelBrowser.contentWindow,
michael@0 116 injectInDocument: true
michael@0 117 });
michael@0 118
michael@0 119 function onWebPanelSidebarUnload() {
michael@0 120 windowNS(window).onWebPanelSidebarUnload = null;
michael@0 121
michael@0 122 // uncheck the associated menuitem
michael@0 123 bar.setAttribute('checked', 'false');
michael@0 124
michael@0 125 emit(self, 'hide', {});
michael@0 126 emit(self, 'detach', worker);
michael@0 127 windowNS(window).worker = null;
michael@0 128 }
michael@0 129 windowNS(window).onWebPanelSidebarUnload = onWebPanelSidebarUnload;
michael@0 130 panelBrowser.contentWindow.addEventListener('unload', onWebPanelSidebarUnload, true);
michael@0 131
michael@0 132 // check the associated menuitem
michael@0 133 bar.setAttribute('checked', 'true');
michael@0 134
michael@0 135 function onWebPanelSidebarReady() {
michael@0 136 panelBrowser.contentWindow.removeEventListener('DOMContentLoaded', onWebPanelSidebarReady, false);
michael@0 137 windowNS(window).onWebPanelSidebarReady = null;
michael@0 138
michael@0 139 emit(self, 'ready', worker);
michael@0 140 }
michael@0 141 windowNS(window).onWebPanelSidebarReady = onWebPanelSidebarReady;
michael@0 142 panelBrowser.contentWindow.addEventListener('DOMContentLoaded', onWebPanelSidebarReady, false);
michael@0 143
michael@0 144 function onWebPanelSidebarLoad() {
michael@0 145 panelBrowser.contentWindow.removeEventListener('load', onWebPanelSidebarLoad, true);
michael@0 146 windowNS(window).onWebPanelSidebarLoad = null;
michael@0 147
michael@0 148 // TODO: decide if returning worker is acceptable..
michael@0 149 //emit(self, 'show', { worker: worker });
michael@0 150 emit(self, 'show', {});
michael@0 151 }
michael@0 152 windowNS(window).onWebPanelSidebarLoad = onWebPanelSidebarLoad;
michael@0 153 panelBrowser.contentWindow.addEventListener('load', onWebPanelSidebarLoad, true);
michael@0 154
michael@0 155 emit(self, 'attach', worker);
michael@0 156 }
michael@0 157 windowNS(window).onWebPanelSidebarCreated = onWebPanelSidebarCreated;
michael@0 158 panelBrowser.addEventListener('DOMWindowCreated', onWebPanelSidebarCreated, true);
michael@0 159 }
michael@0 160 windowNS(window).onSidebarLoad = onSidebarLoad;
michael@0 161 sidebar.addEventListener('load', onSidebarLoad, true); // removed properly
michael@0 162 },
michael@0 163 onUntrack: function(window) {
michael@0 164 if (!isBrowser(window))
michael@0 165 return;
michael@0 166
michael@0 167 // hide the sidebar if it is showing
michael@0 168 hideSidebar(window, self);
michael@0 169
michael@0 170 // kill the menu item
michael@0 171 let { bar } = windowNS(window);
michael@0 172 if (bar) {
michael@0 173 removeFromArray(viewsFor(self), bar);
michael@0 174 dispose(bar);
michael@0 175 }
michael@0 176
michael@0 177 // kill listeners
michael@0 178 let sidebar = window.document.getElementById('sidebar');
michael@0 179
michael@0 180 if (windowNS(window).onSidebarLoad) {
michael@0 181 sidebar && sidebar.removeEventListener('load', windowNS(window).onSidebarLoad, true)
michael@0 182 windowNS(window).onSidebarLoad = null;
michael@0 183 }
michael@0 184
michael@0 185 let panelBrowser = sidebar && sidebar.contentDocument.getElementById(WEB_PANEL_BROWSER_ID);
michael@0 186 if (windowNS(window).onWebPanelSidebarCreated) {
michael@0 187 panelBrowser && panelBrowser.removeEventListener('DOMWindowCreated', windowNS(window).onWebPanelSidebarCreated, true);
michael@0 188 windowNS(window).onWebPanelSidebarCreated = null;
michael@0 189 }
michael@0 190
michael@0 191 if (windowNS(window).onWebPanelSidebarReady) {
michael@0 192 panelBrowser && panelBrowser.contentWindow.removeEventListener('DOMContentLoaded', windowNS(window).onWebPanelSidebarReady, false);
michael@0 193 windowNS(window).onWebPanelSidebarReady = null;
michael@0 194 }
michael@0 195
michael@0 196 if (windowNS(window).onWebPanelSidebarLoad) {
michael@0 197 panelBrowser && panelBrowser.contentWindow.removeEventListener('load', windowNS(window).onWebPanelSidebarLoad, true);
michael@0 198 windowNS(window).onWebPanelSidebarLoad = null;
michael@0 199 }
michael@0 200
michael@0 201 if (windowNS(window).onWebPanelSidebarUnload) {
michael@0 202 panelBrowser && panelBrowser.contentWindow.removeEventListener('unload', windowNS(window).onWebPanelSidebarUnload, true);
michael@0 203 windowNS(window).onWebPanelSidebarUnload();
michael@0 204 }
michael@0 205 }
michael@0 206 });
michael@0 207
michael@0 208 views.set(this, bars);
michael@0 209
michael@0 210 add(sidebars, this);
michael@0 211 },
michael@0 212 get id() (modelFor(this) || {}).id,
michael@0 213 get title() (modelFor(this) || {}).title,
michael@0 214 set title(v) {
michael@0 215 // destroyed?
michael@0 216 if (!modelFor(this))
michael@0 217 return;
michael@0 218 // validation
michael@0 219 if (typeof v != 'string')
michael@0 220 throw Error('title must be a string');
michael@0 221 validateTitleAndURLCombo(this, v, this.url);
michael@0 222 // do update
michael@0 223 updateTitle(this, v);
michael@0 224 return modelFor(this).title = v;
michael@0 225 },
michael@0 226 get url() (modelFor(this) || {}).url,
michael@0 227 set url(v) {
michael@0 228 // destroyed?
michael@0 229 if (!modelFor(this))
michael@0 230 return;
michael@0 231
michael@0 232 // validation
michael@0 233 if (!isLocalURL(v))
michael@0 234 throw Error('the url must be a valid local url');
michael@0 235
michael@0 236 validateTitleAndURLCombo(this, this.title, v);
michael@0 237
michael@0 238 // do update
michael@0 239 updateURL(this, v);
michael@0 240 modelFor(this).url = v;
michael@0 241 },
michael@0 242 show: function() {
michael@0 243 return showSidebar(null, this);
michael@0 244 },
michael@0 245 hide: function() {
michael@0 246 return hideSidebar(null, this);
michael@0 247 },
michael@0 248 dispose: function() {
michael@0 249 const internals = sidebarNS(this);
michael@0 250
michael@0 251 off(this);
michael@0 252
michael@0 253 remove(sidebars, this);
michael@0 254
michael@0 255 // stop tracking windows
michael@0 256 if (internals.tracker) {
michael@0 257 internals.tracker.unload();
michael@0 258 }
michael@0 259
michael@0 260 internals.tracker = null;
michael@0 261 internals.windowNS = null;
michael@0 262
michael@0 263 views.delete(this);
michael@0 264 models.delete(this);
michael@0 265 }
michael@0 266 });
michael@0 267 exports.Sidebar = Sidebar;
michael@0 268
michael@0 269 function validateTitleAndURLCombo(sidebar, title, url) {
michael@0 270 if (sidebar.title == title && sidebar.url == url) {
michael@0 271 return false;
michael@0 272 }
michael@0 273
michael@0 274 for (let window of windows(null, { includePrivate: true })) {
michael@0 275 let sidebar = window.document.querySelector('menuitem[sidebarurl="' + url + '"][label="' + title + '"]');
michael@0 276 if (sidebar) {
michael@0 277 throw Error('The provided title and url combination is invalid (already used).');
michael@0 278 }
michael@0 279 }
michael@0 280
michael@0 281 return false;
michael@0 282 }
michael@0 283
michael@0 284 isShowing.define(Sidebar, isSidebarShowing.bind(null, null));
michael@0 285 show.define(Sidebar, showSidebar.bind(null, null));
michael@0 286 hide.define(Sidebar, hideSidebar.bind(null, null));
michael@0 287
michael@0 288 identify.define(Sidebar, function(sidebar) {
michael@0 289 return sidebar.id;
michael@0 290 });
michael@0 291
michael@0 292 function toggleSidebar(window, sidebar) {
michael@0 293 // TODO: make sure this is not private
michael@0 294 window = window || getMostRecentBrowserWindow();
michael@0 295 if (isSidebarShowing(window, sidebar)) {
michael@0 296 return hideSidebar(window, sidebar);
michael@0 297 }
michael@0 298 return showSidebar(window, sidebar);
michael@0 299 }
michael@0 300 toggle.define(Sidebar, toggleSidebar.bind(null, null));

mercurial