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 |