// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /** * @typedef {{device: string, * lastUpdateTime: string, * opened: boolean, * separatorIndexes: !Array, * timestamp: number, * tabs: !Array, * tag: string}} */ let ForeignDeviceInternal; Polymer({ is: 'history-synced-device-manager', properties: { /** * @type {?Array} */ sessionList: { type: Array, observer: 'updateSyncedDevices', }, searchTerm: { type: String, observer: 'searchTermChanged', }, /** * An array of synced devices with synced tab data. * @type {!Array} */ syncedDevices_: { type: Array, value: function() { return []; }, }, /** @private */ signInState: { type: Boolean, observer: 'signInStateChanged_', }, /** @private */ guestSession_: { type: Boolean, value: loadTimeData.getBoolean('isGuestSession'), }, /** @private */ fetchingSyncedTabs_: { type: Boolean, value: false, }, /** @private */ hasSeenForeignData_: Boolean, /** * The session ID referring to the currently active action menu. * @private {?string} */ actionMenuModel_: String, }, listeners: { 'open-menu': 'onOpenMenu_', 'update-focus-grid': 'updateFocusGrid_', }, /** @type {?cr.ui.FocusGrid} */ focusGrid_: null, /** @override */ attached: function() { this.focusGrid_ = new cr.ui.FocusGrid(); // Update the sign in state. chrome.send('otherDevicesInitialized'); md_history.BrowserService.getInstance().recordHistogram( SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.INITIALIZED, SyncedTabsHistogram.LIMIT); }, /** @override */ detached: function() { this.focusGrid_.destroy(); }, /** @return {HTMLElement} */ getContentScrollTarget: function() { return this; }, /** * @param {!ForeignSession} session * @return {!ForeignDeviceInternal} * @private */ createInternalDevice_: function(session) { let tabs = []; const separatorIndexes = []; for (let i = 0; i < session.windows.length; i++) { const windowId = session.windows[i].sessionId; const newTabs = session.windows[i].tabs; if (newTabs.length == 0) continue; newTabs.forEach(function(tab) { tab.windowId = windowId; }); let windowAdded = false; if (!this.searchTerm) { // Add all the tabs if there is no search term. tabs = tabs.concat(newTabs); windowAdded = true; } else { const searchText = this.searchTerm.toLowerCase(); for (let j = 0; j < newTabs.length; j++) { const tab = newTabs[j]; if (tab.title.toLowerCase().indexOf(searchText) != -1) { tabs.push(tab); windowAdded = true; } } } if (windowAdded && i != session.windows.length - 1) separatorIndexes.push(tabs.length - 1); } return { device: session.name, lastUpdateTime: '– ' + session.modifiedTime, opened: true, separatorIndexes: separatorIndexes, timestamp: session.timestamp, tabs: tabs, tag: session.tag, }; }, /** @private */ onSignInTap_: function() { chrome.send('startSignInFlow'); }, /** @private */ onOpenMenu_: function(e) { const menu = /** @type {CrActionMenuElement} */ (this.$.menu.get()); this.actionMenuModel_ = e.detail.tag; menu.showAt(e.detail.target); md_history.BrowserService.getInstance().recordHistogram( SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.SHOW_SESSION_MENU, SyncedTabsHistogram.LIMIT); }, /** @private */ onOpenAllTap_: function() { const menu = assert(this.$.menu.getIfExists()); const browserService = md_history.BrowserService.getInstance(); browserService.recordHistogram( SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.OPEN_ALL, SyncedTabsHistogram.LIMIT); browserService.openForeignSessionAllTabs(assert(this.actionMenuModel_)); this.actionMenuModel_ = null; menu.close(); }, /** @private */ updateFocusGrid_: function() { if (!this.focusGrid_) return; this.focusGrid_.destroy(); this.debounce('updateFocusGrid', () => { Array.from(this.shadowRoot.querySelectorAll('history-synced-device-card')) .reduce((prev, cur) => prev.concat(cur.createFocusRows()), []) .forEach((row) => { this.focusGrid_.addRow(row); }); this.focusGrid_.ensureRowActive(1); }); }, /** @private */ onDeleteSessionTap_: function() { const menu = assert(this.$.menu.getIfExists()); const browserService = md_history.BrowserService.getInstance(); browserService.recordHistogram( SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.HIDE_FOR_NOW, SyncedTabsHistogram.LIMIT); browserService.deleteForeignSession(assert(this.actionMenuModel_)); this.actionMenuModel_ = null; menu.close(); }, /** @private */ clearDisplayedSyncedDevices_: function() { this.syncedDevices_ = []; }, /** * Decide whether or not should display no synced tabs message. * @param {boolean} signInState * @param {number} syncedDevicesLength * @param {boolean} guestSession * @return {boolean} */ showNoSyncedMessage: function( signInState, syncedDevicesLength, guestSession) { if (guestSession) return true; return signInState && syncedDevicesLength == 0; }, /** * Shows the signin guide when the user is not signed in and not in a guest * session. * @param {boolean} signInState * @param {boolean} guestSession * @return {boolean} */ showSignInGuide: function(signInState, guestSession) { const show = !signInState && !guestSession; if (show) { md_history.BrowserService.getInstance().recordAction( 'Signin_Impression_FromRecentTabs'); } return show; }, /** * Decide what message should be displayed when user is logged in and there * are no synced tabs. * @return {string} */ noSyncedTabsMessage: function() { let stringName = this.fetchingSyncedTabs_ ? 'loading' : 'noSyncedResults'; if (this.searchTerm !== '') stringName = 'noSearchResults'; return loadTimeData.getString(stringName); }, /** * Replaces the currently displayed synced tabs with |sessionList|. It is * common for only a single session within the list to have changed, We try to * avoid doing extra work in this case. The logic could be more intelligent * about updating individual tabs rather than replacing whole sessions, but * this approach seems to have acceptable performance. * @param {?Array} sessionList */ updateSyncedDevices: function(sessionList) { this.fetchingSyncedTabs_ = false; if (!sessionList) return; if (sessionList.length > 0 && !this.hasSeenForeignData_) { this.hasSeenForeignData_ = true; md_history.BrowserService.getInstance().recordHistogram( SYNCED_TABS_HISTOGRAM_NAME, SyncedTabsHistogram.HAS_FOREIGN_DATA, SyncedTabsHistogram.LIMIT); } const devices = []; sessionList.forEach((session) => { const device = this.createInternalDevice_(session); if (device.tabs.length != 0) devices.push(device); }); this.syncedDevices_ = devices; }, /** * Get called when user's sign in state changes, this will affect UI of synced * tabs page. Sign in promo gets displayed when user is signed out, and * different messages are shown when there are no synced tabs. */ signInStateChanged_: function() { this.fire('history-view-changed'); // User signed out, clear synced device list and show the sign in promo. if (!this.signInState) { this.clearDisplayedSyncedDevices_(); return; } // User signed in, show the loading message when querying for synced // devices. this.fetchingSyncedTabs_ = true; }, searchTermChanged: function(searchTerm) { this.clearDisplayedSyncedDevices_(); this.updateSyncedDevices(this.sessionList); } });