Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 "use strict";
6 var Appbar = {
7 get starButton() { return document.getElementById('star-button'); },
8 get pinButton() { return document.getElementById('pin-button'); },
9 get menuButton() { return document.getElementById('menu-button'); },
11 // track selected/active richgrid/tilegroup - the context for contextual action buttons
12 activeTileset: null,
14 init: function Appbar_init() {
15 // fired from appbar bindings
16 Elements.contextappbar.addEventListener('MozAppbarShowing', this, false);
17 Elements.contextappbar.addEventListener('MozAppbarDismissing', this, false);
19 // fired when a context sensitive item (bookmarks) changes state
20 window.addEventListener('MozContextActionsChange', this, false);
22 // browser events we need to update button state on
23 Elements.browsers.addEventListener('URLChanged', this, true);
24 Elements.tabList.addEventListener('TabSelect', this, true);
26 // tilegroup selection events for all modules get bubbled up
27 window.addEventListener("selectionchange", this, false);
28 Services.obs.addObserver(this, "metro_on_async_tile_created", false);
30 // gather appbar telemetry data
31 try {
32 UITelemetry.addSimpleMeasureFunction("metro-appbar",
33 this.getAppbarMeasures.bind(this));
34 } catch (ex) {
35 // swallow exception that occurs if metro-appbar measure is already set up
36 }
37 },
39 observe: function(aSubject, aTopic, aData) {
40 switch (aTopic) {
41 case "metro_on_async_tile_created":
42 this._updatePinButton();
43 break;
44 }
45 },
47 handleEvent: function Appbar_handleEvent(aEvent) {
48 switch (aEvent.type) {
49 case 'URLChanged':
50 case 'TabSelect':
51 this.update();
52 this.flushActiveTileset(aEvent.lastTab);
53 break;
55 case 'MozAppbarShowing':
56 this.update();
57 break;
59 case 'MozAppbarDismissing':
60 if (this.activeTileset && ('isBound' in this.activeTileset)) {
61 this.activeTileset.clearSelection();
62 }
63 this._clearContextualActions();
64 this.activeTileset = null;
65 break;
67 case 'MozContextActionsChange':
68 let actions = aEvent.actions;
69 let setName = aEvent.target.contextSetName;
70 // could transition in old, new buttons?
71 this.showContextualActions(actions, setName);
72 break;
74 case "selectionchange":
75 let nodeName = aEvent.target.nodeName;
76 if ('richgrid' === nodeName) {
77 this._onTileSelectionChanged(aEvent);
78 }
79 break;
80 }
81 },
83 flushActiveTileset: function flushActiveTileset(aTab) {
84 try {
85 let tab = aTab || Browser.selectedTab;
86 // Switching away from or loading a site into a startui tab that has actions
87 // pending, we consider this confirmation that the user wants to flush changes.
88 if (this.activeTileset && tab && tab.browser && tab.browser.currentURI.spec == kStartURI) {
89 ContextUI.dismiss();
90 }
91 } catch (ex) {}
92 },
94 shutdown: function shutdown() {
95 this.flushActiveTileset();
96 },
98 /*
99 * Called from various places when the visible content
100 * has changed such that button states may need to be
101 * updated.
102 */
103 update: function update() {
104 this._updatePinButton();
105 this._updateStarButton();
106 },
108 onPinButton: function() {
109 if (this.pinButton.checked) {
110 Browser.pinSite();
111 } else {
112 Browser.unpinSite();
113 }
114 },
116 onStarButton: function(aValue) {
117 if (aValue === undefined) {
118 aValue = this.starButton.checked;
119 }
121 if (aValue) {
122 Browser.starSite(function () {
123 Appbar._updateStarButton();
124 });
125 } else {
126 Browser.unstarSite(function () {
127 Appbar._updateStarButton();
128 });
129 }
130 },
132 onMenuButton: function(aEvent) {
133 let typesArray = [];
135 if (BrowserUI.isPrivateBrowsingEnabled) {
136 typesArray.push("private-browsing");
137 }
138 if (!BrowserUI.isStartTabVisible) {
139 typesArray.push("find-in-page");
140 if (ContextCommands.getPageSource())
141 typesArray.push("view-page-source");
142 }
143 if (ContextCommands.getStoreLink())
144 typesArray.push("ms-meta-data");
145 if (ConsolePanelView.enabled)
146 typesArray.push("open-error-console");
147 if (!Services.metro.immersive)
148 typesArray.push("open-jsshell");
150 typesArray.push("view-on-desktop");
152 var x = this.menuButton.getBoundingClientRect().left;
153 var y = Elements.toolbar.getBoundingClientRect().top;
154 ContextMenuUI.showContextMenu({
155 json: {
156 types: typesArray,
157 string: '',
158 xPos: x,
159 yPos: y,
160 leftAligned: true,
161 bottomAligned: true
162 }
164 });
165 },
167 onViewOnDesktop: function() {
168 let appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"].
169 getService(Components.interfaces.nsIAppStartup);
171 Services.prefs.setBoolPref('browser.sessionstore.resume_session_once', true);
172 this._incrementCountableEvent("switch-to-desktop-button");
173 appStartup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit |
174 Components.interfaces.nsIAppStartup.eRestart);
175 },
177 onAutocompleteCloseButton: function () {
178 Elements.autocomplete.closePopup();
179 },
181 dispatchContextualAction: function(aActionName){
182 let activeTileset = this.activeTileset;
183 if (activeTileset && ('isBound' in this.activeTileset)) {
184 // fire event on the richgrid, others can listen
185 // but we keep coupling loose so grid doesn't need to know about appbar
186 let event = document.createEvent("Events");
187 event.action = aActionName;
188 event.initEvent("context-action", true, true); // is cancelable
189 activeTileset.dispatchEvent(event);
190 if (!event.defaultPrevented) {
191 activeTileset.clearSelection();
192 Elements.contextappbar.dismiss();
193 }
194 }
195 },
197 showContextualActions: function(aVerbs, aSetName) {
198 // When the appbar is not visible, we want the icons to refresh right away
199 let immediate = !Elements.contextappbar.isShowing;
201 if (aVerbs.length) {
202 Elements.contextappbar.show();
203 }
205 // Look up all of the buttons for the verbs that should be visible.
206 let idsToVisibleVerbs = new Map();
207 for (let verb of aVerbs) {
208 let id = verb + "-selected-button";
209 if (!document.getElementById(id)) {
210 throw new Error("Appbar.showContextualActions: no button for " + verb);
211 }
212 idsToVisibleVerbs.set(id, verb);
213 }
215 // Sort buttons into 2 buckets - needing showing and needing hiding.
216 let toHide = [], toShow = [];
217 let buttons = Elements.contextappbar.getElementsByTagName("toolbarbutton");
218 for (let button of buttons) {
219 let verb = idsToVisibleVerbs.get(button.id);
220 if (verb != undefined) {
221 // Button should be visible, and may or may not be showing.
222 this._updateContextualActionLabel(button, verb, aSetName);
223 if (button.hidden) {
224 toShow.push(button);
225 }
226 } else if (!button.hidden) {
227 // Button is visible, but shouldn't be.
228 toHide.push(button);
229 }
230 }
232 if (immediate) {
233 toShow.forEach(function(element) {
234 element.removeAttribute("fade");
235 element.hidden = false;
236 });
237 toHide.forEach(function(element) {
238 element.setAttribute("fade", true);
239 element.hidden = true;
240 });
241 return;
242 }
244 return Task.spawn(function() {
245 if (toHide.length) {
246 yield Util.transitionElementVisibility(toHide, false);
247 }
248 if (toShow.length) {
249 yield Util.transitionElementVisibility(toShow, true);
250 }
251 });
252 },
254 _clearContextualActions: function() {
255 this.showContextualActions([]);
256 },
258 _updateContextualActionLabel: function(aButton, aVerb, aSetName) {
259 // True if the action's label string contains the set name and
260 // thus has to be selected based on the list passed in.
261 let usesSetName = aButton.hasAttribute("label-uses-set-name");
262 let name = "contextAppbar2." + aVerb + (usesSetName ? "." + aSetName : "");
263 aButton.label = Strings.browser.GetStringFromName(name);
264 },
266 _onTileSelectionChanged: function _onTileSelectionChanged(aEvent){
267 let activeTileset = aEvent.target;
269 // deselect tiles in other tile groups,
270 // ensure previousyl-activeTileset is bound before calling methods on it
271 if (this.activeTileset &&
272 ('isBound' in this.activeTileset) &&
273 this.activeTileset !== activeTileset) {
274 this.activeTileset.clearSelection();
275 }
276 // keep track of which view is the target/scope for the contextual actions
277 this.activeTileset = activeTileset;
279 // ask the view for the list verbs/action-names it thinks are
280 // appropriate for the tiles selected
281 let contextActions = activeTileset.contextActions;
282 let verbs = [v for (v of contextActions)];
284 // fire event with these verbs as payload
285 let event = document.createEvent("Events");
286 event.actions = verbs;
287 event.initEvent("MozContextActionsChange", true, false);
288 activeTileset.dispatchEvent(event);
290 if (verbs.length) {
291 Elements.contextappbar.show(); // should be no-op if we're already showing
292 } else {
293 Elements.contextappbar.dismiss();
294 }
295 },
297 // track certain appbar events and interactions for the UITelemetry probe
298 _countableEvents: {},
300 _incrementCountableEvent: function(aName) {
301 if (!(aName in this._countableEvents)) {
302 this._countableEvents[aName] = 0;
303 }
304 this._countableEvents[aName]++;
305 },
307 getAppbarMeasures: function() {
308 return {
309 countableEvents: this._countableEvents
310 };
311 },
313 _updatePinButton: function() {
314 this.pinButton.checked = Browser.isSitePinned();
315 },
317 _updateStarButton: function() {
318 Browser.isSiteStarredAsync(function (isStarred) {
319 this.starButton.checked = isStarred;
320 }.bind(this));
321 },
323 };