| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @fileoverview The menu that shows tabs from sessions on other devices. | 6 * @fileoverview The menu that shows tabs from sessions on other devices. |
| 7 */ | 7 */ |
| 8 | 8 |
| 9 cr.define('ntp', function() { | 9 cr.define('ntp', function() { |
| 10 'use strict'; | 10 'use strict'; |
| 11 | 11 |
| 12 var localStrings = new LocalStrings(); | 12 var localStrings = new LocalStrings(); |
| 13 var Menu = cr.ui.Menu; | 13 var Menu = cr.ui.Menu; |
| 14 var MenuItem = cr.ui.MenuItem; | 14 var MenuItem = cr.ui.MenuItem; |
| 15 var MenuButton = cr.ui.MenuButton; | 15 var MenuButton = cr.ui.MenuButton; |
| 16 var OtherSessionsMenuButton = cr.ui.define('button'); | 16 var OtherSessionsMenuButton = cr.ui.define('button'); |
| 17 | 17 |
| 18 // Histogram buckets for UMA tracking of menu usage. | 18 // Histogram buckets for UMA tracking of menu usage. |
| 19 var HISTOGRAM_EVENT = { | 19 var HISTOGRAM_EVENT = { |
| 20 INITIALIZED: 0, | 20 INITIALIZED: 0, |
| 21 SHOW_MENU: 1, | 21 SHOW_MENU: 1, |
| 22 LINK_CLICKED: 2, | 22 LINK_CLICKED: 2, |
| 23 LINK_RIGHT_CLICKED: 3 | 23 LINK_RIGHT_CLICKED: 3, |
| 24 SESSION_NAME_RIGHT_CLICKED: 4 |
| 24 }; | 25 }; |
| 25 var HISTOGRAM_EVENT_LIMIT = HISTOGRAM_EVENT.LINK_RIGHT_CLICKED + 1; | 26 var HISTOGRAM_EVENT_LIMIT = HISTOGRAM_EVENT.SESSION_NAME_RIGHT_CLICKED + 1; |
| 26 | 27 |
| 27 OtherSessionsMenuButton.prototype = { | 28 OtherSessionsMenuButton.prototype = { |
| 28 __proto__: MenuButton.prototype, | 29 __proto__: MenuButton.prototype, |
| 29 | 30 |
| 30 decorate: function() { | 31 decorate: function() { |
| 31 MenuButton.prototype.decorate.call(this); | 32 MenuButton.prototype.decorate.call(this); |
| 32 this.menu = new Menu; | 33 this.menu = new Menu; |
| 33 cr.ui.decorate(this.menu, Menu); | 34 cr.ui.decorate(this.menu, Menu); |
| 34 this.menu.classList.add('footer-menu'); | 35 this.menu.classList.add('footer-menu'); |
| 35 this.menu.addEventListener('contextmenu', | 36 this.menu.addEventListener('contextmenu', |
| 36 this.onContextMenu_.bind(this), true); | 37 this.onContextMenu_.bind(this), true); |
| 37 document.body.appendChild(this.menu); | 38 document.body.appendChild(this.menu); |
| 38 | 39 |
| 40 // Create the context menu that appears when the user right clicks |
| 41 // on a device name. |
| 42 this.deviceContextMenu_ = DeviceContextMenuController.getInstance().menu; |
| 43 document.body.appendChild(this.deviceContextMenu_); |
| 44 |
| 39 this.promoMessage_ = $('other-sessions-promo-template').cloneNode(true); | 45 this.promoMessage_ = $('other-sessions-promo-template').cloneNode(true); |
| 40 this.promoMessage_.removeAttribute('id'); // Prevent a duplicate id. | 46 this.promoMessage_.removeAttribute('id'); // Prevent a duplicate id. |
| 41 | 47 |
| 42 this.sessions_ = []; | 48 this.sessions_ = []; |
| 43 this.anchorType = cr.ui.AnchorType.ABOVE; | 49 this.anchorType = cr.ui.AnchorType.ABOVE; |
| 44 this.invertLeftRight = true; | 50 this.invertLeftRight = true; |
| 45 | 51 |
| 46 this.recordUmaEvent_(HISTOGRAM_EVENT.INITIALIZED); | 52 this.recordUmaEvent_(HISTOGRAM_EVENT.INITIALIZED); |
| 47 }, | 53 }, |
| 48 | 54 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 60 */ | 66 */ |
| 61 recordUmaEvent_: function(eventId) { | 67 recordUmaEvent_: function(eventId) { |
| 62 chrome.send('metricsHandler:recordInHistogram', | 68 chrome.send('metricsHandler:recordInHistogram', |
| 63 ['NewTabPage.OtherSessionsMenu', eventId, HISTOGRAM_EVENT_LIMIT]); | 69 ['NewTabPage.OtherSessionsMenu', eventId, HISTOGRAM_EVENT_LIMIT]); |
| 64 }, | 70 }, |
| 65 | 71 |
| 66 /** | 72 /** |
| 67 * Handle a context menu event for an object in the menu's DOM subtree. | 73 * Handle a context menu event for an object in the menu's DOM subtree. |
| 68 */ | 74 */ |
| 69 onContextMenu_: function(e) { | 75 onContextMenu_: function(e) { |
| 70 // Only record the action if it occurred in one of the menu items. | 76 // Only record the action if it occurred in one of the menu items or |
| 71 if (findAncestorByClass(e.target, 'footer-menu-item')) | 77 // on one of the session headings. |
| 78 if (findAncestorByClass(e.target, 'footer-menu-item')) { |
| 72 this.recordUmaEvent_(HISTOGRAM_EVENT.LINK_RIGHT_CLICKED); | 79 this.recordUmaEvent_(HISTOGRAM_EVENT.LINK_RIGHT_CLICKED); |
| 80 } else { |
| 81 var heading = findAncestorByClass(e.target, 'session-heading'); |
| 82 if (heading) { |
| 83 this.recordUmaEvent_(HISTOGRAM_EVENT.SESSION_NAME_RIGHT_CLICKED); |
| 84 |
| 85 // Let the context menu know which session it was invoked on, |
| 86 // since they all share the same instance of the menu. |
| 87 DeviceContextMenuController.getInstance().setSessionTag( |
| 88 heading.sessionTag_); |
| 89 } |
| 90 } |
| 73 }, | 91 }, |
| 74 | 92 |
| 75 /** | 93 /** |
| 94 * Hides the menu. |
| 95 * @override |
| 96 */ |
| 97 hideMenu: function() { |
| 98 // Don't hide if the device context menu is currently showing. |
| 99 if (cr.ui.contextMenuHandler.menu != this.deviceContextMenu_) |
| 100 MenuButton.prototype.hideMenu.call(this); |
| 101 }, |
| 102 |
| 103 /** |
| 76 * Shows the menu, first rebuilding it if necessary. | 104 * Shows the menu, first rebuilding it if necessary. |
| 77 * TODO(estade): the right of the menu should align with the right of the | 105 * TODO(estade): the right of the menu should align with the right of the |
| 78 * button. | 106 * button. |
| 79 * @override | 107 * @override |
| 80 */ | 108 */ |
| 81 showMenu: function() { | 109 showMenu: function() { |
| 82 if (this.sessions_.length == 0) | 110 if (this.sessions_.length == 0) |
| 83 chrome.send('getForeignSessions'); | 111 chrome.send('getForeignSessions'); |
| 84 this.recordUmaEvent_(HISTOGRAM_EVENT.SHOW_MENU); | 112 this.recordUmaEvent_(HISTOGRAM_EVENT.SHOW_MENU); |
| 85 MenuButton.prototype.showMenu.call(this); | 113 MenuButton.prototype.showMenu.call(this); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 113 * Add the UI for a foreign session to the menu. | 141 * Add the UI for a foreign session to the menu. |
| 114 * @param {Object} session Object describing the foreign session. | 142 * @param {Object} session Object describing the foreign session. |
| 115 */ | 143 */ |
| 116 addSession_: function(session) { | 144 addSession_: function(session) { |
| 117 var doc = this.ownerDocument; | 145 var doc = this.ownerDocument; |
| 118 | 146 |
| 119 var section = doc.createElement('section'); | 147 var section = doc.createElement('section'); |
| 120 this.menu.appendChild(section); | 148 this.menu.appendChild(section); |
| 121 | 149 |
| 122 var heading = doc.createElement('h3'); | 150 var heading = doc.createElement('h3'); |
| 151 heading.className = 'session-heading'; |
| 123 heading.textContent = session.name; | 152 heading.textContent = session.name; |
| 153 heading.sessionTag_ = session.tag; |
| 124 section.appendChild(heading); | 154 section.appendChild(heading); |
| 125 | 155 |
| 156 var timeSpan = doc.createElement('span'); |
| 157 timeSpan.className = 'details'; |
| 158 timeSpan.textContent = session.modifiedTime; |
| 159 heading.appendChild(timeSpan); |
| 160 |
| 161 cr.ui.contextMenuHandler.setContextMenu(heading, |
| 162 this.deviceContextMenu_); |
| 163 |
| 126 for (var i = 0; i < session.windows.length; i++) { | 164 for (var i = 0; i < session.windows.length; i++) { |
| 127 var window = session.windows[i]; | 165 var window = session.windows[i]; |
| 166 |
| 167 // Show a separator between multiple windows in the same session. |
| 168 if (i > 0) |
| 169 section.appendChild(doc.createElement('hr')); |
| 170 |
| 128 for (var j = 0; j < window.tabs.length; j++) { | 171 for (var j = 0; j < window.tabs.length; j++) { |
| 129 var tab = window.tabs[j]; | 172 var tab = window.tabs[j]; |
| 130 var a = doc.createElement('a'); | 173 var a = doc.createElement('a'); |
| 131 a.className = 'footer-menu-item'; | 174 a.className = 'footer-menu-item'; |
| 132 a.textContent = tab.title; | 175 a.textContent = tab.title; |
| 133 a.href = tab.url; | 176 a.href = tab.url; |
| 134 a.style.backgroundImage = 'url(chrome://favicon/' + tab.url + ')'; | 177 a.style.backgroundImage = 'url(chrome://favicon/' + tab.url + ')'; |
| 135 var clickHandler = this.makeClickHandler_( | 178 var clickHandler = this.makeClickHandler_( |
| 136 session.tag, String(window.sessionId), String(tab.sessionId)); | 179 session.tag, String(window.sessionId), String(tab.sessionId)); |
| 137 a.addEventListener('click', clickHandler); | 180 a.addEventListener('click', clickHandler); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 172 * @param {boolean} signedIn Is the user currently signed in? | 215 * @param {boolean} signedIn Is the user currently signed in? |
| 173 */ | 216 */ |
| 174 updateSignInState: function(signedIn) { | 217 updateSignInState: function(signedIn) { |
| 175 if (signedIn) | 218 if (signedIn) |
| 176 chrome.send('getForeignSessions'); | 219 chrome.send('getForeignSessions'); |
| 177 else | 220 else |
| 178 this.classList.add('invisible'); | 221 this.classList.add('invisible'); |
| 179 }, | 222 }, |
| 180 }; | 223 }; |
| 181 | 224 |
| 225 /** |
| 226 * Controller for the context menu for device names in the list of sessions. |
| 227 * This class is designed to be used as a singleton. |
| 228 * |
| 229 * @constructor |
| 230 */ |
| 231 function DeviceContextMenuController() { |
| 232 this.__proto__ = DeviceContextMenuController.prototype; |
| 233 this.initialize(); |
| 234 } |
| 235 cr.addSingletonGetter(DeviceContextMenuController); |
| 236 |
| 237 DeviceContextMenuController.prototype = { |
| 238 |
| 239 initialize: function() { |
| 240 var menu = new cr.ui.Menu; |
| 241 cr.ui.decorate(menu, cr.ui.Menu); |
| 242 menu.classList.add('device-context-menu'); |
| 243 menu.classList.add('footer-menu-context-menu'); |
| 244 this.menu = menu; |
| 245 this.hideItem_ = this.appendMenuItem_('hideSessionMenuItemText'); |
| 246 this.hideItem_.addEventListener('activate', this.onHide_.bind(this)); |
| 247 }, |
| 248 |
| 249 /** |
| 250 * Appends a menu item to |this.menu|. |
| 251 * @param {String} textId The ID for the localized string that acts as |
| 252 * the item's label. |
| 253 */ |
| 254 appendMenuItem_: function(textId) { |
| 255 var button = cr.doc.createElement('button'); |
| 256 this.menu.appendChild(button); |
| 257 cr.ui.decorate(button, cr.ui.MenuItem); |
| 258 button.textContent = localStrings.getString(textId); |
| 259 return button; |
| 260 }, |
| 261 |
| 262 /** |
| 263 * Handler for the 'hide' menu item. |
| 264 * @param {Event} e The activation event. |
| 265 * @private |
| 266 */ |
| 267 onHide_: function(e) { |
| 268 chrome.send('deleteForeignSession', [this.sessionTag_]); |
| 269 chrome.send('getForeignSessions'); // Refresh the list. |
| 270 }, |
| 271 |
| 272 /** |
| 273 * Set the session tag which identifies the session that the context menu |
| 274 * was invoked on. |
| 275 * @param {String} tag The session tag. |
| 276 */ |
| 277 setSessionTag: function(tag) { |
| 278 this.sessionTag_ = tag; |
| 279 } |
| 280 }; |
| 281 |
| 182 return { | 282 return { |
| 183 OtherSessionsMenuButton: OtherSessionsMenuButton, | 283 OtherSessionsMenuButton: OtherSessionsMenuButton, |
| 184 }; | 284 }; |
| 185 }); | 285 }); |
| OLD | NEW |