summaryrefslogtreecommitdiffstats
path: root/browser/components/syncedtabs
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/syncedtabs')
-rw-r--r--browser/components/syncedtabs/EventEmitter.jsm45
-rw-r--r--browser/components/syncedtabs/SyncedTabsDeckComponent.js169
-rw-r--r--browser/components/syncedtabs/SyncedTabsDeckStore.js60
-rw-r--r--browser/components/syncedtabs/SyncedTabsDeckView.js116
-rw-r--r--browser/components/syncedtabs/SyncedTabsListStore.js235
-rw-r--r--browser/components/syncedtabs/TabListComponent.js142
-rw-r--r--browser/components/syncedtabs/TabListView.js568
-rw-r--r--browser/components/syncedtabs/jar.mn7
-rw-r--r--browser/components/syncedtabs/moz.build17
-rw-r--r--browser/components/syncedtabs/sidebar.js30
-rw-r--r--browser/components/syncedtabs/sidebar.xhtml114
-rw-r--r--browser/components/syncedtabs/util.js23
12 files changed, 0 insertions, 1526 deletions
diff --git a/browser/components/syncedtabs/EventEmitter.jsm b/browser/components/syncedtabs/EventEmitter.jsm
deleted file mode 100644
index ec3225f0f..000000000
--- a/browser/components/syncedtabs/EventEmitter.jsm
+++ /dev/null
@@ -1,45 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-this.EXPORTED_SYMBOLS = [
- "EventEmitter"
-];
-
-// Simple event emitter abstraction for storage objects to use.
-function EventEmitter () {
- this._events = new Map();
-}
-
-EventEmitter.prototype = {
- on(event, listener) {
- if (this._events.has(event)) {
- this._events.get(event).add(listener);
- } else {
- this._events.set(event, new Set([listener]));
- }
- },
- off(event, listener) {
- if (!this._events.has(event)) {
- return;
- }
- this._events.get(event).delete(listener);
- },
- emit(event, ...args) {
- if (!this._events.has(event)) {
- return;
- }
- for (let listener of this._events.get(event).values()) {
- try {
- listener.apply(this, args);
- } catch (e) {
- Cu.reportError(e);
- }
- }
- },
-};
-
diff --git a/browser/components/syncedtabs/SyncedTabsDeckComponent.js b/browser/components/syncedtabs/SyncedTabsDeckComponent.js
deleted file mode 100644
index c35277795..000000000
--- a/browser/components/syncedtabs/SyncedTabsDeckComponent.js
+++ /dev/null
@@ -1,169 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-Cu.import("resource:///modules/syncedtabs/SyncedTabsDeckStore.js");
-Cu.import("resource:///modules/syncedtabs/SyncedTabsDeckView.js");
-Cu.import("resource:///modules/syncedtabs/SyncedTabsListStore.js");
-Cu.import("resource:///modules/syncedtabs/TabListComponent.js");
-Cu.import("resource:///modules/syncedtabs/TabListView.js");
-let { getChromeWindow } = Cu.import("resource:///modules/syncedtabs/util.js", {});
-
-XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function () {
- return Components.utils.import("resource://gre/modules/FxAccountsCommon.js", {});
-});
-
-let log = Cu.import("resource://gre/modules/Log.jsm", {})
- .Log.repository.getLogger("Sync.RemoteTabs");
-
-this.EXPORTED_SYMBOLS = [
- "SyncedTabsDeckComponent"
-];
-
-/* SyncedTabsDeckComponent
- * This component instantiates views and storage objects as well as defines
- * behaviors that will be passed down to the views. This helps keep the views
- * isolated and easier to test.
- */
-
-function SyncedTabsDeckComponent({
- window, SyncedTabs, fxAccounts, deckStore, listStore, listComponent, DeckView, getChromeWindowMock,
-}) {
- this._window = window;
- this._SyncedTabs = SyncedTabs;
- this._fxAccounts = fxAccounts;
- this._DeckView = DeckView || SyncedTabsDeckView;
- // used to stub during tests
- this._getChromeWindow = getChromeWindowMock || getChromeWindow;
-
- this._deckStore = deckStore || new SyncedTabsDeckStore();
- this._syncedTabsListStore = listStore || new SyncedTabsListStore(SyncedTabs);
- this.tabListComponent = listComponent || new TabListComponent({
- window: this._window,
- store: this._syncedTabsListStore,
- View: TabListView,
- SyncedTabs: SyncedTabs,
- clipboardHelper: Cc["@mozilla.org/widget/clipboardhelper;1"]
- .getService(Ci.nsIClipboardHelper),
- getChromeWindow: this._getChromeWindow,
- });
-}
-
-SyncedTabsDeckComponent.prototype = {
- PANELS: {
- TABS_CONTAINER: "tabs-container",
- TABS_FETCHING: "tabs-fetching",
- NOT_AUTHED_INFO: "notAuthedInfo",
- SINGLE_DEVICE_INFO: "singleDeviceInfo",
- TABS_DISABLED: "tabs-disabled",
- },
-
- get container() {
- return this._deckView ? this._deckView.container : null;
- },
-
- init() {
- Services.obs.addObserver(this, this._SyncedTabs.TOPIC_TABS_CHANGED, false);
- Services.obs.addObserver(this, FxAccountsCommon.ONLOGIN_NOTIFICATION, false);
-
- // Go ahead and trigger sync
- this._SyncedTabs.syncTabs()
- .catch(Cu.reportError);
-
- this._deckView = new this._DeckView(this._window, this.tabListComponent, {
- onAndroidClick: event => this.openAndroidLink(event),
- oniOSClick: event => this.openiOSLink(event),
- onSyncPrefClick: event => this.openSyncPrefs(event)
- });
-
- this._deckStore.on("change", state => this._deckView.render(state));
- // Trigger the initial rendering of the deck view
- // Object.values only in nightly
- this._deckStore.setPanels(Object.keys(this.PANELS).map(k => this.PANELS[k]));
- // Set the initial panel to display
- this.updatePanel();
- },
-
- uninit() {
- Services.obs.removeObserver(this, this._SyncedTabs.TOPIC_TABS_CHANGED);
- Services.obs.removeObserver(this, FxAccountsCommon.ONLOGIN_NOTIFICATION);
- this._deckView.destroy();
- },
-
- observe(subject, topic, data) {
- switch (topic) {
- case this._SyncedTabs.TOPIC_TABS_CHANGED:
- this._syncedTabsListStore.getData();
- this.updatePanel();
- break;
- case FxAccountsCommon.ONLOGIN_NOTIFICATION:
- this.updatePanel();
- break;
- default:
- break;
- }
- },
-
- // There's no good way to mock fxAccounts in browser tests where it's already
- // been instantiated, so we have this method for stubbing.
- _accountStatus() {
- return this._fxAccounts.accountStatus();
- },
-
- getPanelStatus() {
- return this._accountStatus().then(exists => {
- if (!exists) {
- return this.PANELS.NOT_AUTHED_INFO;
- }
- if (!this._SyncedTabs.isConfiguredToSyncTabs) {
- return this.PANELS.TABS_DISABLED;
- }
- if (!this._SyncedTabs.hasSyncedThisSession) {
- return this.PANELS.TABS_FETCHING;
- }
- return this._SyncedTabs.getTabClients().then(clients => {
- if (clients.length) {
- return this.PANELS.TABS_CONTAINER;
- }
- return this.PANELS.SINGLE_DEVICE_INFO;
- });
- })
- .catch(err => {
- Cu.reportError(err);
- return this.PANELS.NOT_AUTHED_INFO;
- });
- },
-
- updatePanel() {
- // return promise for tests
- return this.getPanelStatus()
- .then(panelId => this._deckStore.selectPanel(panelId))
- .catch(Cu.reportError);
- },
-
- openAndroidLink(event) {
- let href = Services.prefs.getCharPref("identity.mobilepromo.android") + "synced-tabs-sidebar";
- this._openUrl(href, event);
- },
-
- openiOSLink(event) {
- let href = Services.prefs.getCharPref("identity.mobilepromo.ios") + "synced-tabs-sidebar";
- this._openUrl(href, event);
- },
-
- _openUrl(url, event) {
- this._window.openUILink(url, event);
- },
-
- openSyncPrefs() {
- this._getChromeWindow(this._window).gSyncUI.openSetup(null, "tabs-sidebar");
- }
-};
-
diff --git a/browser/components/syncedtabs/SyncedTabsDeckStore.js b/browser/components/syncedtabs/SyncedTabsDeckStore.js
deleted file mode 100644
index ede6914c8..000000000
--- a/browser/components/syncedtabs/SyncedTabsDeckStore.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-let { EventEmitter } = Cu.import("resource:///modules/syncedtabs/EventEmitter.jsm", {});
-
-this.EXPORTED_SYMBOLS = [
- "SyncedTabsDeckStore"
-];
-
-/**
- * SyncedTabsDeckStore
- *
- * This store keeps track of the deck view state, including the panels and which
- * one is selected. The view listens for change events on the store, which are
- * triggered whenever the state changes. If it's a small change, the state
- * will have `isUpdatable` set to true so the view can skip rerendering the whole
- * DOM.
- */
-function SyncedTabsDeckStore() {
- EventEmitter.call(this);
- this._panels = [];
-}
-
-Object.assign(SyncedTabsDeckStore.prototype, EventEmitter.prototype, {
- _change(isUpdatable = false) {
- let panels = this._panels.map(panel => {
- return {id: panel, selected: panel === this._selectedPanel};
- });
- this.emit("change", {panels, isUpdatable: isUpdatable});
- },
-
- /**
- * Sets the selected panelId and triggers a change event.
- * @param {String} panelId - ID of the panel to select.
- */
- selectPanel(panelId) {
- if (this._panels.indexOf(panelId) === -1 || this._selectedPanel === panelId) {
- return;
- }
- this._selectedPanel = panelId;
- this._change(true);
- },
-
- /**
- * Update the set of panels in the deck and trigger a change event.
- * @param {Array} panels - an array of IDs for each panel in the deck.
- */
- setPanels(panels) {
- if (panels === this._panels) {
- return;
- }
- this._panels = panels || [];
- this._change();
- }
-});
diff --git a/browser/components/syncedtabs/SyncedTabsDeckView.js b/browser/components/syncedtabs/SyncedTabsDeckView.js
deleted file mode 100644
index e9efff323..000000000
--- a/browser/components/syncedtabs/SyncedTabsDeckView.js
+++ /dev/null
@@ -1,116 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-let { getChromeWindow } = Cu.import("resource:///modules/syncedtabs/util.js", {});
-
-let log = Cu.import("resource://gre/modules/Log.jsm", {})
- .Log.repository.getLogger("Sync.RemoteTabs");
-
-this.EXPORTED_SYMBOLS = [
- "SyncedTabsDeckView"
-];
-
-/**
- * SyncedTabsDeckView
- *
- * Instances of SyncedTabsDeckView render DOM nodes from a given state.
- * No state is kept internaly and the DOM will completely
- * rerender unless the state flags `isUpdatable`, which helps
- * make small changes without the overhead of a full rerender.
- */
-const SyncedTabsDeckView = function (window, tabListComponent, props) {
- this.props = props;
-
- this._window = window;
- this._doc = window.document;
-
- this._tabListComponent = tabListComponent;
- this._deckTemplate = this._doc.getElementById("deck-template");
- this.container = this._doc.createElement("div");
-};
-
-SyncedTabsDeckView.prototype = {
- render(state) {
- if (state.isUpdatable) {
- this.update(state);
- } else {
- this.create(state);
- }
- },
-
- create(state) {
- let deck = this._doc.importNode(this._deckTemplate.content, true).firstElementChild;
- this._clearChilden();
-
- let tabListWrapper = this._doc.createElement("div");
- tabListWrapper.className = "tabs-container sync-state";
- this._tabListComponent.init();
- tabListWrapper.appendChild(this._tabListComponent.container);
- deck.appendChild(tabListWrapper);
- this.container.appendChild(deck);
-
- this._generateDevicePromo();
-
- this._attachListeners();
- this.update(state);
- },
-
- _getBrowserBundle() {
- return getChromeWindow(this._window).document.getElementById("bundle_browser");
- },
-
- _generateDevicePromo() {
- let bundle = this._getBrowserBundle();
- let formatArgs = ["android", "ios"].map(os => {
- let link = this._doc.createElement("a");
- link.textContent = bundle.getString(`appMenuRemoteTabs.mobilePromo.${os}`);
- link.className = `${os}-link text-link`;
- link.setAttribute("href", "#");
- return link.outerHTML;
- });
- // Put it all together...
- let contents = bundle.getFormattedString("appMenuRemoteTabs.mobilePromo.text2", formatArgs);
- this.container.querySelector(".device-promo").innerHTML = contents;
- },
-
- destroy() {
- this._tabListComponent.uninit();
- this.container.remove();
- },
-
- update(state) {
- // Note that we may also want to update elements that are outside of the
- // deck, so use the document to find the class names rather than our
- // container.
- for (let panel of state.panels) {
- if (panel.selected) {
- Array.prototype.map.call(this._doc.getElementsByClassName(panel.id),
- item => item.classList.add("selected"));
- } else {
- Array.prototype.map.call(this._doc.getElementsByClassName(panel.id),
- item => item.classList.remove("selected"));
- }
- }
- },
-
- _clearChilden() {
- while (this.container.firstChild) {
- this.container.removeChild(this.container.firstChild);
- }
- },
-
- _attachListeners() {
- this.container.querySelector(".android-link").addEventListener("click", this.props.onAndroidClick);
- this.container.querySelector(".ios-link").addEventListener("click", this.props.oniOSClick);
- let syncPrefLinks = this.container.querySelectorAll(".sync-prefs");
- for (let link of syncPrefLinks) {
- link.addEventListener("click", this.props.onSyncPrefClick);
- }
- },
-};
-
diff --git a/browser/components/syncedtabs/SyncedTabsListStore.js b/browser/components/syncedtabs/SyncedTabsListStore.js
deleted file mode 100644
index 8f03d9a89..000000000
--- a/browser/components/syncedtabs/SyncedTabsListStore.js
+++ /dev/null
@@ -1,235 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-let { EventEmitter } = Cu.import("resource:///modules/syncedtabs/EventEmitter.jsm", {});
-
-this.EXPORTED_SYMBOLS = [
- "SyncedTabsListStore"
-];
-
-/**
- * SyncedTabsListStore
- *
- * Instances of this store encapsulate all of the state associated with a synced tabs list view.
- * The state includes the clients, their tabs, the row that is currently selected,
- * and the filtered query.
- */
-function SyncedTabsListStore(SyncedTabs) {
- EventEmitter.call(this);
- this._SyncedTabs = SyncedTabs;
- this.data = [];
- this._closedClients = {};
- this._selectedRow = [-1, -1];
- this.filter = "";
- this.inputFocused = false;
-}
-
-Object.assign(SyncedTabsListStore.prototype, EventEmitter.prototype, {
- // This internal method triggers the "change" event that views
- // listen for. It denormalizes the state so that it's easier for
- // the view to deal with. updateType hints to the view what
- // actually needs to be rerendered or just updated, and can be
- // empty (to (re)render everything), "searchbox" (to rerender just the tab list),
- // or "all" (to skip rendering and just update all attributes of existing nodes).
- _change(updateType) {
- let selectedParent = this._selectedRow[0];
- let selectedChild = this._selectedRow[1];
- let rowSelected = false;
- // clone the data so that consumers can't mutate internal storage
- let data = Cu.cloneInto(this.data, {});
- let tabCount = 0;
-
- data.forEach((client, index) => {
- client.closed = !!this._closedClients[client.id];
-
- if (rowSelected || selectedParent < 0) {
- return;
- }
- if (this.filter) {
- if (selectedParent < tabCount + client.tabs.length) {
- client.tabs[selectedParent - tabCount].selected = true;
- client.tabs[selectedParent - tabCount].focused = !this.inputFocused;
- rowSelected = true;
- } else {
- tabCount += client.tabs.length;
- }
- return;
- }
- if (selectedParent === index && selectedChild === -1) {
- client.selected = true;
- client.focused = !this.inputFocused;
- rowSelected = true;
- } else if (selectedParent === index) {
- client.tabs[selectedChild].selected = true;
- client.tabs[selectedChild].focused = !this.inputFocused;
- rowSelected = true;
- }
- });
-
- // If this were React the view would be smart enough
- // to not re-render the whole list unless necessary. But it's
- // not, so updateType is a hint to the view of what actually
- // needs to be rerendered.
- this.emit("change", {
- clients: data,
- canUpdateAll: updateType === "all",
- canUpdateInput: updateType === "searchbox",
- filter: this.filter,
- inputFocused: this.inputFocused
- });
- },
-
- /**
- * Moves the row selection from a child to its parent,
- * which occurs when the parent of a selected row closes.
- */
- _selectParentRow() {
- this._selectedRow[1] = -1;
- },
-
- _toggleBranch(id, closed) {
- this._closedClients[id] = closed;
- if (this._closedClients[id]) {
- this._selectParentRow();
- }
- this._change("all");
- },
-
- _isOpen(client) {
- return !this._closedClients[client.id];
- },
-
- moveSelectionDown() {
- let branchRow = this._selectedRow[0];
- let childRow = this._selectedRow[1];
- let branch = this.data[branchRow];
-
- if (this.filter) {
- this.selectRow(branchRow + 1);
- return;
- }
-
- if (branchRow < 0) {
- this.selectRow(0, -1);
- } else if ((!branch.tabs.length || childRow >= branch.tabs.length - 1 || !this._isOpen(branch)) && branchRow < this.data.length) {
- this.selectRow(branchRow + 1, -1);
- } else if (childRow < branch.tabs.length) {
- this.selectRow(branchRow, childRow + 1);
- }
- },
-
- moveSelectionUp() {
- let branchRow = this._selectedRow[0];
- let childRow = this._selectedRow[1];
-
- if (this.filter) {
- this.selectRow(branchRow - 1);
- return;
- }
-
- if (branchRow < 0) {
- this.selectRow(0, -1);
- } else if (childRow < 0 && branchRow > 0) {
- let prevBranch = this.data[branchRow - 1];
- let newChildRow = this._isOpen(prevBranch) ? prevBranch.tabs.length - 1 : -1;
- this.selectRow(branchRow - 1, newChildRow);
- } else if (childRow >= 0) {
- this.selectRow(branchRow, childRow - 1);
- }
- },
-
- // Selects a row and makes sure the selection is within bounds
- selectRow(parent, child) {
- let maxParentRow = this.filter ? this._tabCount() : this.data.length;
- let parentRow = parent;
- if (parent <= -1) {
- parentRow = 0;
- } else if (parent >= maxParentRow) {
- return;
- }
-
- let childRow = child;
- if (parentRow === -1 || this.filter || typeof child === "undefined" || child < -1) {
- childRow = -1;
- } else if (child >= this.data[parentRow].tabs.length) {
- childRow = this.data[parentRow].tabs.length - 1;
- }
-
- if (this._selectedRow[0] === parentRow && this._selectedRow[1] === childRow) {
- return;
- }
-
- this._selectedRow = [parentRow, childRow];
- this.inputFocused = false;
- this._change("all");
- },
-
- _tabCount() {
- return this.data.reduce((prev, curr) => curr.tabs.length + prev, 0);
- },
-
- toggleBranch(id) {
- this._toggleBranch(id, !this._closedClients[id]);
- },
-
- closeBranch(id) {
- this._toggleBranch(id, true);
- },
-
- openBranch(id) {
- this._toggleBranch(id, false);
- },
-
- focusInput() {
- this.inputFocused = true;
- // A change type of "all" updates rather than rebuilds, which is what we
- // want here - only the selection/focus has changed.
- this._change("all");
- },
-
- blurInput() {
- this.inputFocused = false;
- // A change type of "all" updates rather than rebuilds, which is what we
- // want here - only the selection/focus has changed.
- this._change("all");
- },
-
- clearFilter() {
- this.filter = "";
- this._selectedRow = [-1, -1];
- return this.getData();
- },
-
- // Fetches data from the SyncedTabs module and triggers
- // and update
- getData(filter) {
- let updateType;
- let hasFilter = typeof filter !== "undefined";
- if (hasFilter) {
- this.filter = filter;
- this._selectedRow = [-1, -1];
-
- // When a filter is specified we tell the view that only the list
- // needs to be rerendered so that it doesn't disrupt the input
- // field's focus.
- updateType = "searchbox";
- }
-
- // return promise for tests
- return this._SyncedTabs.getTabClients(this.filter)
- .then(result => {
- if (!hasFilter) {
- // Only sort clients and tabs if we're rendering the whole list.
- this._SyncedTabs.sortTabClientsByLastUsed(result);
- }
- this.data = result;
- this._change(updateType);
- })
- .catch(Cu.reportError);
- }
-});
diff --git a/browser/components/syncedtabs/TabListComponent.js b/browser/components/syncedtabs/TabListComponent.js
deleted file mode 100644
index d3aace8f9..000000000
--- a/browser/components/syncedtabs/TabListComponent.js
+++ /dev/null
@@ -1,142 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-let log = Cu.import("resource://gre/modules/Log.jsm", {})
- .Log.repository.getLogger("Sync.RemoteTabs");
-
-XPCOMUtils.defineLazyModuleGetter(this, "BrowserUITelemetry",
- "resource:///modules/BrowserUITelemetry.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesUIUtils",
- "resource:///modules/PlacesUIUtils.jsm");
-
-this.EXPORTED_SYMBOLS = [
- "TabListComponent"
-];
-
-/**
- * TabListComponent
- *
- * The purpose of this component is to compose the view, state, and actions.
- * It defines high level actions that act on the state and passes them to the
- * view for it to trigger during user interaction. It also subscribes the view
- * to state changes so it can rerender.
- */
-
-function TabListComponent({window, store, View, SyncedTabs, clipboardHelper,
- getChromeWindow}) {
- this._window = window;
- this._store = store;
- this._View = View;
- this._clipboardHelper = clipboardHelper;
- this._getChromeWindow = getChromeWindow;
- // used to trigger Sync from context menu
- this._SyncedTabs = SyncedTabs;
-}
-
-TabListComponent.prototype = {
- get container() {
- return this._view.container;
- },
-
- init() {
- log.debug("Initializing TabListComponent");
-
- this._view = new this._View(this._window, {
- onSelectRow: (...args) => this.onSelectRow(...args),
- onOpenTab: (...args) => this.onOpenTab(...args),
- onOpenTabs: (...args) => this.onOpenTabs(...args),
- onMoveSelectionDown: (...args) => this.onMoveSelectionDown(...args),
- onMoveSelectionUp: (...args) => this.onMoveSelectionUp(...args),
- onToggleBranch: (...args) => this.onToggleBranch(...args),
- onBookmarkTab: (...args) => this.onBookmarkTab(...args),
- onCopyTabLocation: (...args) => this.onCopyTabLocation(...args),
- onSyncRefresh: (...args) => this.onSyncRefresh(...args),
- onFilter: (...args) => this.onFilter(...args),
- onClearFilter: (...args) => this.onClearFilter(...args),
- onFilterFocus: (...args) => this.onFilterFocus(...args),
- onFilterBlur: (...args) => this.onFilterBlur(...args)
- });
-
- this._store.on("change", state => this._view.render(state));
- this._view.render({clients: []});
- // get what's already available...
- this._store.getData();
- this._store.focusInput();
- },
-
- uninit() {
- this._view.destroy();
- },
-
- onFilter(query) {
- this._store.getData(query);
- },
-
- onClearFilter() {
- this._store.clearFilter();
- },
-
- onFilterFocus() {
- this._store.focusInput();
- },
-
- onFilterBlur() {
- this._store.blurInput();
- },
-
- onSelectRow(position) {
- this._store.selectRow(position[0], position[1]);
- },
-
- onMoveSelectionDown() {
- this._store.moveSelectionDown();
- },
-
- onMoveSelectionUp() {
- this._store.moveSelectionUp();
- },
-
- onToggleBranch(id) {
- this._store.toggleBranch(id);
- },
-
- onBookmarkTab(uri, title) {
- this._window.top.PlacesCommandHook
- .bookmarkLink(this._window.top.PlacesUtils.bookmarksMenuFolderId, uri, title)
- .catch(Cu.reportError);
- },
-
- onOpenTab(url, where, params) {
- this._window.openUILinkIn(url, where, params);
- BrowserUITelemetry.countSyncedTabEvent("open", "sidebar");
- },
-
- onOpenTabs(urls, where) {
- if (!PlacesUIUtils.confirmOpenInTabs(urls.length, this._window)) {
- return;
- }
- if (where == "window") {
- this._window.openDialog(this._window.getBrowserURL(), "_blank",
- "chrome,dialog=no,all", urls.join("|"));
- } else {
- let loadInBackground = where == "tabshifted" ? true : false;
- this._getChromeWindow(this._window).gBrowser.loadTabs(urls, loadInBackground, false);
- }
- BrowserUITelemetry.countSyncedTabEvent("openmultiple", "sidebar");
- },
-
- onCopyTabLocation(url) {
- this._clipboardHelper.copyString(url);
- },
-
- onSyncRefresh() {
- this._SyncedTabs.syncTabs(true);
- }
-};
diff --git a/browser/components/syncedtabs/TabListView.js b/browser/components/syncedtabs/TabListView.js
deleted file mode 100644
index dab15101b..000000000
--- a/browser/components/syncedtabs/TabListView.js
+++ /dev/null
@@ -1,568 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/Services.jsm");
-
-let { getChromeWindow } = Cu.import("resource:///modules/syncedtabs/util.js", {});
-
-let log = Cu.import("resource://gre/modules/Log.jsm", {})
- .Log.repository.getLogger("Sync.RemoteTabs");
-
-this.EXPORTED_SYMBOLS = [
- "TabListView"
-];
-
-function getContextMenu(window) {
- return getChromeWindow(window).document.getElementById("SyncedTabsSidebarContext");
-}
-
-function getTabsFilterContextMenu(window) {
- return getChromeWindow(window).document.getElementById("SyncedTabsSidebarTabsFilterContext");
-}
-
-/*
- * TabListView
- *
- * Given a state, this object will render the corresponding DOM.
- * It maintains no state of it's own. It listens for DOM events
- * and triggers actions that may cause the state to change and
- * ultimately the view to rerender.
- */
-function TabListView(window, props) {
- this.props = props;
-
- this._window = window;
- this._doc = this._window.document;
-
- this._tabsContainerTemplate = this._doc.getElementById("tabs-container-template");
- this._clientTemplate = this._doc.getElementById("client-template");
- this._emptyClientTemplate = this._doc.getElementById("empty-client-template");
- this._tabTemplate = this._doc.getElementById("tab-template");
- this.tabsFilter = this._doc.querySelector(".tabsFilter");
- this.clearFilter = this._doc.querySelector(".textbox-search-clear");
- this.searchBox = this._doc.querySelector(".search-box");
- this.searchIcon = this._doc.querySelector(".textbox-search-icon");
-
- this.container = this._doc.createElement("div");
-
- this._attachFixedListeners();
-
- this._setupContextMenu();
-}
-
-TabListView.prototype = {
- render(state) {
- // Don't rerender anything; just update attributes, e.g. selection
- if (state.canUpdateAll) {
- this._update(state);
- return;
- }
- // Rerender the tab list
- if (state.canUpdateInput) {
- this._updateSearchBox(state);
- this._createList(state);
- return;
- }
- // Create the world anew
- this._create(state);
- },
-
- // Create the initial DOM from templates
- _create(state) {
- let wrapper = this._doc.importNode(this._tabsContainerTemplate.content, true).firstElementChild;
- this._clearChilden();
- this.container.appendChild(wrapper);
-
- this.list = this.container.querySelector(".list");
-
- this._createList(state);
- this._updateSearchBox(state);
-
- this._attachListListeners();
- },
-
- _createList(state) {
- this._clearChilden(this.list);
- for (let client of state.clients) {
- if (state.filter) {
- this._renderFilteredClient(client);
- } else {
- this._renderClient(client);
- }
- }
- if (this.list.firstChild) {
- const firstTab = this.list.firstChild.querySelector(".item.tab:first-child .item-title");
- if (firstTab) {
- firstTab.setAttribute("tabindex", 2);
- }
- }
- },
-
- destroy() {
- this._teardownContextMenu();
- this.container.remove();
- },
-
- _update(state) {
- this._updateSearchBox(state);
- for (let client of state.clients) {
- let clientNode = this._doc.getElementById("item-" + client.id);
- if (clientNode) {
- this._updateClient(client, clientNode);
- }
-
- client.tabs.forEach((tab, index) => {
- let tabNode = this._doc.getElementById('tab-' + client.id + '-' + index);
- this._updateTab(tab, tabNode, index);
- });
- }
- },
-
- // Client rows are hidden when the list is filtered
- _renderFilteredClient(client, filter) {
- client.tabs.forEach((tab, index) => {
- let node = this._renderTab(client, tab, index);
- this.list.appendChild(node);
- });
- },
-
- _renderClient(client) {
- let itemNode = client.tabs.length ?
- this._createClient(client) :
- this._createEmptyClient(client);
-
- this._updateClient(client, itemNode);
-
- let tabsList = itemNode.querySelector(".item-tabs-list");
- client.tabs.forEach((tab, index) => {
- let node = this._renderTab(client, tab, index);
- tabsList.appendChild(node);
- });
-
- this.list.appendChild(itemNode);
- return itemNode;
- },
-
- _renderTab(client, tab, index) {
- let itemNode = this._createTab(tab);
- this._updateTab(tab, itemNode, index);
- return itemNode;
- },
-
- _createClient(item) {
- return this._doc.importNode(this._clientTemplate.content, true).firstElementChild;
- },
-
- _createEmptyClient(item) {
- return this._doc.importNode(this._emptyClientTemplate.content, true).firstElementChild;
- },
-
- _createTab(item) {
- return this._doc.importNode(this._tabTemplate.content, true).firstElementChild;
- },
-
- _clearChilden(node) {
- let parent = node || this.container;
- while (parent.firstChild) {
- parent.removeChild(parent.firstChild);
- }
- },
-
- // These listeners are attached only once, when we initialize the view
- _attachFixedListeners() {
- this.tabsFilter.addEventListener("input", this.onFilter.bind(this));
- this.tabsFilter.addEventListener("focus", this.onFilterFocus.bind(this));
- this.tabsFilter.addEventListener("blur", this.onFilterBlur.bind(this));
- this.clearFilter.addEventListener("click", this.onClearFilter.bind(this));
- this.searchIcon.addEventListener("click", this.onFilterFocus.bind(this));
- },
-
- // These listeners have to be re-created every time since we re-create the list
- _attachListListeners() {
- this.list.addEventListener("click", this.onClick.bind(this));
- this.list.addEventListener("mouseup", this.onMouseUp.bind(this));
- this.list.addEventListener("keydown", this.onKeyDown.bind(this));
- },
-
- _updateSearchBox(state) {
- if (state.filter) {
- this.searchBox.classList.add("filtered");
- } else {
- this.searchBox.classList.remove("filtered");
- }
- this.tabsFilter.value = state.filter;
- if (state.inputFocused) {
- this.searchBox.setAttribute("focused", true);
- this.tabsFilter.focus();
- } else {
- this.searchBox.removeAttribute("focused");
- }
- },
-
- /**
- * Update the element representing an item, ensuring it's in sync with the
- * underlying data.
- * @param {client} item - Item to use as a source.
- * @param {Element} itemNode - Element to update.
- */
- _updateClient(item, itemNode) {
- itemNode.setAttribute("id", "item-" + item.id);
- let lastSync = new Date(item.lastModified);
- let lastSyncTitle = getChromeWindow(this._window).gSyncUI.formatLastSyncDate(lastSync);
- itemNode.setAttribute("title", lastSyncTitle);
- if (item.closed) {
- itemNode.classList.add("closed");
- } else {
- itemNode.classList.remove("closed");
- }
- if (item.selected) {
- itemNode.classList.add("selected");
- } else {
- itemNode.classList.remove("selected");
- }
- if (item.isMobile) {
- itemNode.classList.add("device-image-mobile");
- } else {
- itemNode.classList.add("device-image-desktop");
- }
- if (item.focused) {
- itemNode.focus();
- }
- itemNode.dataset.id = item.id;
- itemNode.querySelector(".item-title").textContent = item.name;
- },
-
- /**
- * Update the element representing a tab, ensuring it's in sync with the
- * underlying data.
- * @param {tab} item - Item to use as a source.
- * @param {Element} itemNode - Element to update.
- */
- _updateTab(item, itemNode, index) {
- itemNode.setAttribute("title", `${item.title}\n${item.url}`);
- itemNode.setAttribute("id", "tab-" + item.client + '-' + index);
- if (item.selected) {
- itemNode.classList.add("selected");
- } else {
- itemNode.classList.remove("selected");
- }
- if (item.focused) {
- itemNode.focus();
- }
- itemNode.dataset.url = item.url;
-
- itemNode.querySelector(".item-title").textContent = item.title;
-
- if (item.icon) {
- let icon = itemNode.querySelector(".item-icon-container");
- icon.style.backgroundImage = "url(" + item.icon + ")";
- }
- },
-
- onMouseUp(event) {
- if (event.which == 2) { // Middle click
- this.onClick(event);
- }
- },
-
- onClick(event) {
- let itemNode = this._findParentItemNode(event.target);
- if (!itemNode) {
- return;
- }
-
- if (itemNode.classList.contains("tab")) {
- let url = itemNode.dataset.url;
- if (url) {
- this.onOpenSelected(url, event);
- }
- }
-
- // Middle click on a client
- if (itemNode.classList.contains("client")) {
- let where = getChromeWindow(this._window).whereToOpenLink(event);
- if (where != "current") {
- const tabs = itemNode.querySelector(".item-tabs-list").childNodes;
- const urls = [...tabs].map(tab => tab.dataset.url);
- this.props.onOpenTabs(urls, where);
- }
- }
-
- if (event.target.classList.contains("item-twisty-container")
- && event.which != 2) {
- this.props.onToggleBranch(itemNode.dataset.id);
- return;
- }
-
- let position = this._getSelectionPosition(itemNode);
- this.props.onSelectRow(position);
- },
-
- /**
- * Handle a keydown event on the list box.
- * @param {Event} event - Triggering event.
- */
- onKeyDown(event) {
- if (event.keyCode == this._window.KeyEvent.DOM_VK_DOWN) {
- event.preventDefault();
- this.props.onMoveSelectionDown();
- } else if (event.keyCode == this._window.KeyEvent.DOM_VK_UP) {
- event.preventDefault();
- this.props.onMoveSelectionUp();
- } else if (event.keyCode == this._window.KeyEvent.DOM_VK_RETURN) {
- let selectedNode = this.container.querySelector('.item.selected');
- if (selectedNode.dataset.url) {
- this.onOpenSelected(selectedNode.dataset.url, event);
- } else if (selectedNode) {
- this.props.onToggleBranch(selectedNode.dataset.id);
- }
- }
- },
-
- onBookmarkTab() {
- let item = this._getSelectedTabNode();
- if (item) {
- let title = item.querySelector(".item-title").textContent;
- this.props.onBookmarkTab(item.dataset.url, title);
- }
- },
-
- onCopyTabLocation() {
- let item = this._getSelectedTabNode();
- if (item) {
- this.props.onCopyTabLocation(item.dataset.url);
- }
- },
-
- onOpenSelected(url, event) {
- let where = getChromeWindow(this._window).whereToOpenLink(event);
- this.props.onOpenTab(url, where, {});
- },
-
- onOpenSelectedFromContextMenu(event) {
- let item = this._getSelectedTabNode();
- if (item) {
- let where = event.target.getAttribute("where");
- let params = {
- private: event.target.hasAttribute("private"),
- };
- this.props.onOpenTab(item.dataset.url, where, params);
- }
- },
-
- onFilter(event) {
- let query = event.target.value;
- if (query) {
- this.props.onFilter(query);
- } else {
- this.props.onClearFilter();
- }
- },
-
- onClearFilter() {
- this.props.onClearFilter();
- },
-
- onFilterFocus() {
- this.props.onFilterFocus();
- },
- onFilterBlur() {
- this.props.onFilterBlur();
- },
-
- _getSelectedTabNode() {
- let item = this.container.querySelector('.item.selected');
- if (this._isTab(item) && item.dataset.url) {
- return item;
- }
- return null;
- },
-
- // Set up the custom context menu
- _setupContextMenu() {
- Services.els.addSystemEventListener(this._window, "contextmenu", this, false);
- for (let getMenu of [getContextMenu, getTabsFilterContextMenu]) {
- let menu = getMenu(this._window);
- menu.addEventListener("popupshowing", this, true);
- menu.addEventListener("command", this, true);
- }
- },
-
- _teardownContextMenu() {
- // Tear down context menu
- Services.els.removeSystemEventListener(this._window, "contextmenu", this, false);
- for (let getMenu of [getContextMenu, getTabsFilterContextMenu]) {
- let menu = getMenu(this._window);
- menu.removeEventListener("popupshowing", this, true);
- menu.removeEventListener("command", this, true);
- }
- },
-
- handleEvent(event) {
- switch (event.type) {
- case "contextmenu":
- this.handleContextMenu(event);
- break;
-
- case "popupshowing": {
- if (event.target.getAttribute("id") == "SyncedTabsSidebarTabsFilterContext") {
- this.handleTabsFilterContextMenuShown(event);
- }
- break;
- }
-
- case "command": {
- let menu = event.target.closest("menupopup");
- switch (menu.getAttribute("id")) {
- case "SyncedTabsSidebarContext":
- this.handleContentContextMenuCommand(event);
- break;
-
- case "SyncedTabsSidebarTabsFilterContext":
- this.handleTabsFilterContextMenuCommand(event);
- break;
- }
- break;
- }
- }
- },
-
- handleTabsFilterContextMenuShown(event) {
- let document = event.target.ownerDocument;
- let focusedElement = document.commandDispatcher.focusedElement;
- if (focusedElement != this.tabsFilter) {
- this.tabsFilter.focus();
- }
- for (let item of event.target.children) {
- if (!item.hasAttribute("cmd")) {
- continue;
- }
- let command = item.getAttribute("cmd");
- let controller = document.commandDispatcher.getControllerForCommand(command);
- if (controller.isCommandEnabled(command)) {
- item.removeAttribute("disabled");
- } else {
- item.setAttribute("disabled", "true");
- }
- }
- },
-
- handleContentContextMenuCommand(event) {
- let id = event.target.getAttribute("id");
- switch (id) {
- case "syncedTabsOpenSelected":
- case "syncedTabsOpenSelectedInTab":
- case "syncedTabsOpenSelectedInWindow":
- case "syncedTabsOpenSelectedInPrivateWindow":
- this.onOpenSelectedFromContextMenu(event);
- break;
- case "syncedTabsBookmarkSelected":
- this.onBookmarkTab();
- break;
- case "syncedTabsCopySelected":
- this.onCopyTabLocation();
- break;
- case "syncedTabsRefresh":
- case "syncedTabsRefreshFilter":
- this.props.onSyncRefresh();
- break;
- }
- },
-
- handleTabsFilterContextMenuCommand(event) {
- let command = event.target.getAttribute("cmd");
- let dispatcher = getChromeWindow(this._window).document.commandDispatcher;
- let controller = dispatcher.focusedElement.controllers.getControllerForCommand(command);
- controller.doCommand(command);
- },
-
- handleContextMenu(event) {
- let menu;
-
- if (event.target == this.tabsFilter) {
- menu = getTabsFilterContextMenu(this._window);
- } else {
- let itemNode = this._findParentItemNode(event.target);
- if (itemNode) {
- let position = this._getSelectionPosition(itemNode);
- this.props.onSelectRow(position);
- }
- menu = getContextMenu(this._window);
- this.adjustContextMenu(menu);
- }
-
- menu.openPopupAtScreen(event.screenX, event.screenY, true, event);
- },
-
- adjustContextMenu(menu) {
- let item = this.container.querySelector('.item.selected');
- let showTabOptions = this._isTab(item);
-
- let el = menu.firstChild;
-
- while (el) {
- if (showTabOptions || el.getAttribute("id") === "syncedTabsRefresh") {
- el.hidden = false;
- } else {
- el.hidden = true;
- }
-
- el = el.nextSibling;
- }
- },
-
- /**
- * Find the parent item element, from a given child element.
- * @param {Element} node - Child element.
- * @return {Element} Element for the item, or null if not found.
- */
- _findParentItemNode(node) {
- while (node && node !== this.list && node !== this._doc.documentElement &&
- !node.classList.contains("item")) {
- node = node.parentNode;
- }
-
- if (node !== this.list && node !== this._doc.documentElement) {
- return node;
- }
-
- return null;
- },
-
- _findParentBranchNode(node) {
- while (node && !node.classList.contains("list") && node !== this._doc.documentElement &&
- !node.parentNode.classList.contains("list")) {
- node = node.parentNode;
- }
-
- if (node !== this.list && node !== this._doc.documentElement) {
- return node;
- }
-
- return null;
- },
-
- _getSelectionPosition(itemNode) {
- let parent = this._findParentBranchNode(itemNode);
- let parentPosition = this._indexOfNode(parent.parentNode, parent);
- let childPosition = -1;
- // if the node is not a client, find its position within the parent
- if (parent !== itemNode) {
- childPosition = this._indexOfNode(itemNode.parentNode, itemNode);
- }
- return [parentPosition, childPosition];
- },
-
- _indexOfNode(parent, child) {
- return Array.prototype.indexOf.call(parent.childNodes, child);
- },
-
- _isTab(item) {
- return item && item.classList.contains("tab");
- }
-};
diff --git a/browser/components/syncedtabs/jar.mn b/browser/components/syncedtabs/jar.mn
deleted file mode 100644
index ba2b105a1..000000000
--- a/browser/components/syncedtabs/jar.mn
+++ /dev/null
@@ -1,7 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-browser.jar:
- content/browser/syncedtabs/sidebar.xhtml
- content/browser/syncedtabs/sidebar.js
diff --git a/browser/components/syncedtabs/moz.build b/browser/components/syncedtabs/moz.build
deleted file mode 100644
index a6515d6a1..000000000
--- a/browser/components/syncedtabs/moz.build
+++ /dev/null
@@ -1,17 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-JAR_MANIFESTS += ['jar.mn']
-
-EXTRA_JS_MODULES.syncedtabs += [
- 'EventEmitter.jsm',
- 'SyncedTabsDeckComponent.js',
- 'SyncedTabsDeckStore.js',
- 'SyncedTabsDeckView.js',
- 'SyncedTabsListStore.js',
- 'TabListComponent.js',
- 'TabListView.js',
- 'util.js',
-]
-
diff --git a/browser/components/syncedtabs/sidebar.js b/browser/components/syncedtabs/sidebar.js
deleted file mode 100644
index 84df95e9d..000000000
--- a/browser/components/syncedtabs/sidebar.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://services-sync/SyncedTabs.jsm");
-Cu.import("resource:///modules/syncedtabs/SyncedTabsDeckComponent.js");
-
-XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
- "resource://gre/modules/FxAccounts.jsm");
-
-this.syncedTabsDeckComponent = new SyncedTabsDeckComponent({window, SyncedTabs, fxAccounts});
-
-let onLoaded = () => {
- syncedTabsDeckComponent.init();
- document.getElementById("template-container").appendChild(syncedTabsDeckComponent.container);
-};
-
-let onUnloaded = () => {
- removeEventListener("DOMContentLoaded", onLoaded);
- removeEventListener("unload", onUnloaded);
- syncedTabsDeckComponent.uninit();
-};
-
-addEventListener("DOMContentLoaded", onLoaded);
-addEventListener("unload", onUnloaded);
diff --git a/browser/components/syncedtabs/sidebar.xhtml b/browser/components/syncedtabs/sidebar.xhtml
deleted file mode 100644
index 3efcbea0e..000000000
--- a/browser/components/syncedtabs/sidebar.xhtml
+++ /dev/null
@@ -1,114 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
- - License, v. 2.0. If a copy of the MPL was not distributed with this
- - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" [
- <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
- %browserDTD;
- <!ENTITY % globalDTD
- SYSTEM "chrome://global/locale/global.dtd">
- %globalDTD;
- <!ENTITY % syncBrandDTD
- SYSTEM "chrome://browser/locale/syncBrand.dtd">
- %syncBrandDTD;
-]>
-<html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
- <head>
- <script src="chrome://browser/content/syncedtabs/sidebar.js" type="application/javascript;version=1.8"></script>
- <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
-
- <link rel="stylesheet" type="text/css" media="all" href="chrome://browser/skin/syncedtabs/sidebar.css"/>
- <link rel="stylesheet" type="text/css" media="all" href="chrome://global/skin/"/>
- <link rel="stylesheet" type="text/css" media="all" href="chrome://global/skin/textbox.css"/>
- <link rel="stylesheet" type="text/css" media="all" href="chrome://browser/content/browser.css"/>
- <title>&syncedTabs.sidebar.label;</title>
- </head>
-
- <body dir="&locale.dir;" role="application">
- <template id="client-template">
- <div class="item client" role="option" tabindex="-1">
- <div class="item-title-container">
- <div class="item-twisty-container"></div>
- <div class="item-icon-container"></div>
- <p class="item-title"></p>
- </div>
- <div class="item-tabs-list"></div>
- </div>
- </template>
- <template id="empty-client-template">
- <div class="item empty client" role="option" tabindex="-1">
- <div class="item-title-container">
- <div class="item-twisty-container"></div>
- <div class="item-icon-container"></div>
- <p class="item-title"></p>
- </div>
- <div class="item-tabs-list">
- <div class="item empty" role="option" tabindex="-1">
- <div class="item-title-container">
- <div class="item-icon-container"></div>
- <p class="item-title">&syncedTabs.sidebar.notabs.label;</p>
- </div>
- </div>
- </div>
- </div>
- </template>
- <template id="tab-template">
- <div class="item tab" role="option" tabindex="-1">
- <div class="item-title-container">
- <div class="item-icon-container"></div>
- <p class="item-title"></p>
- </div>
- </div>
- </template>
-
- <template id="tabs-container-template">
- <div class="tabs-container">
- <div class="list" role="listbox"></div>
- </div>
- </template>
-
- <template id="deck-template">
- <div class="deck">
- <div class="tabs-fetching sync-state">
- <!-- Show intentionally blank panel, see bug 1239845 -->
- </div>
- <div class="notAuthedInfo sync-state">
- <p>&syncedTabs.sidebar.notsignedin.label;</p>
- <p><a href="#" class="sync-prefs text-link">&fxaSignIn.label;</a></p>
- </div>
- <div class="singleDeviceInfo sync-state">
- <p>&syncedTabs.sidebar.noclients.title;</p>
- <p>&syncedTabs.sidebar.noclients.subtitle;</p>
- <p class="device-promo" fxAccountsBrand="&syncBrand.fxAccount.label;"></p>
- </div>
- <div class="tabs-disabled sync-state">
- <p>&syncedTabs.sidebar.tabsnotsyncing.label;</p>
- <p><a href="#" class="sync-prefs text-link">&syncedTabs.sidebar.openprefs.label;</a></p>
- </div>
- </div>
- </template>
-
- <div class="content-container">
- <!-- the non-scrollable header -->
- <div class="content-header">
- <div class="sidebar-search-container tabs-container sync-state">
- <div class="search-box compact">
- <div class="textbox-input-box">
- <input type="text" class="tabsFilter textbox-input" tabindex="1"/>
- <div class="textbox-search-icons">
- <a class="textbox-search-clear"></a>
- <a class="textbox-search-icon"></a>
- </div>
- </div>
- </div>
- </div>
- </div>
- <!-- the scrollable content area where our templates are inserted -->
- <div id="template-container" class="content-scrollable" tabindex="-1">
- </div>
- </div>
- </body>
-</html>
diff --git a/browser/components/syncedtabs/util.js b/browser/components/syncedtabs/util.js
deleted file mode 100644
index e09a1a528..000000000
--- a/browser/components/syncedtabs/util.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-this.EXPORTED_SYMBOLS = [
- "getChromeWindow"
-];
-
-// Get the chrome (ie, browser) window hosting this content.
-function getChromeWindow(window) {
- return window
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsIDocShellTreeItem)
- .rootTreeItem
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindow)
- .wrappedJSObject;
-}