Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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/. */
5 // Fired when the tabtray is displayed
6 const kContextUITabsShowEvent = "MozContextUITabsShow";
7 // add more as needed...
9 /*
10 * Manages context UI (navbar, tabbar, appbar) and track visibility. Also
11 * tracks events that summon and hide the context UI.
12 */
13 var ContextUI = {
14 _expandable: true,
15 _hidingId: 0,
17 /*******************************************
18 * init
19 */
21 init: function init() {
22 Elements.browsers.addEventListener('URLChanged', this, true);
23 Elements.browsers.addEventListener("AlertActive", this, true);
24 Elements.browsers.addEventListener("AlertClose", this, true);
25 Elements.tabList.addEventListener('TabSelect', this, true);
26 Elements.panelUI.addEventListener('ToolPanelShown', this, false);
27 Elements.panelUI.addEventListener('ToolPanelHidden', this, false);
29 Elements.tray.addEventListener("mousemove", this, false);
30 Elements.tray.addEventListener("mouseleave", this, false);
32 window.addEventListener("touchstart", this, true);
33 window.addEventListener("mousedown", this, true);
34 window.addEventListener("MozEdgeUIStarted", this, true);
35 window.addEventListener("MozEdgeUICanceled", this, true);
36 window.addEventListener("MozEdgeUICompleted", this, true);
37 window.addEventListener("keypress", this, true);
38 window.addEventListener("KeyboardChanged", this, false);
39 window.addEventListener("MozFlyoutPanelShowing", this, false);
41 Elements.tray.addEventListener("transitionend", this, true);
43 Appbar.init();
44 },
46 /*******************************************
47 * Context UI state getters & setters
48 */
50 // any visiblilty
51 get isVisible() {
52 return this.navbarVisible || this.tabbarVisible || this.contextAppbarVisible;
53 },
55 // navbar visiblilty
56 get navbarVisible() {
57 return (Elements.navbar.hasAttribute("visible") ||
58 Elements.navbar.hasAttribute("startpage"));
59 },
61 // tabbar visiblilty
62 get tabbarVisible() {
63 return Elements.tray.hasAttribute("expanded");
64 },
66 // appbar visiblilty
67 get contextAppbarVisible() {
68 return Elements.contextappbar.isShowing;
69 },
71 // currently not in use, for the always show tabs feature
72 get isExpandable() { return this._expandable; },
73 set isExpandable(aFlag) {
74 this._expandable = aFlag;
75 if (!this._expandable)
76 this.dismiss();
77 },
79 /*******************************************
80 * Public api
81 */
83 /*
84 * Toggle the current nav UI state. Fires context ui events.
85 */
86 toggleNavUI: function () {
87 // The navbar is forced open when the start page is visible. appbar.js
88 // controls the "visible" property, and browser-ui controls the "startpage"
89 // property. Hence we rely on the tabbar for current toggle state.
90 if (this.tabbarVisible) {
91 this.dismiss();
92 } else {
93 this.displayNavUI();
94 }
95 },
97 /*
98 * Show the nav and tabs bar. Returns true if any non-visible UI
99 * was shown. Fires context ui events.
100 */
101 displayNavUI: function () {
102 let shown = false;
104 if (!this.navbarVisible) {
105 BrowserUI.updateURI();
106 this.displayNavbar();
107 shown = true;
108 }
110 if (!this.tabbarVisible) {
111 this.displayTabs();
112 shown = true;
113 }
115 if (shown) {
116 ContentAreaObserver.updateContentArea();
117 }
119 return shown;
120 },
122 /*
123 * Dismiss any context UI currently visible. Returns true if any
124 * visible UI was dismissed. Fires context ui events.
125 */
126 dismiss: function () {
127 let dismissed = false;
129 this._clearDelayedTimeout();
131 // No assurances this will hide the nav bar. It may have the
132 // 'startpage' property set. This removes the 'visible' property.
133 if (this.navbarVisible) {
134 BrowserUI.blurNavBar();
135 this.dismissNavbar();
136 dismissed = true;
137 }
138 if (this.tabbarVisible) {
139 this.dismissTabs();
140 dismissed = true;
141 }
142 if (Elements.contextappbar.isShowing) {
143 this.dismissContextAppbar();
144 dismissed = true;
145 }
147 if (dismissed) {
148 ContentAreaObserver.updateContentArea();
149 }
151 return dismissed;
152 },
154 /*
155 * Briefly show the tab bar and then hide it. Fires context ui events.
156 */
157 peekTabs: function peekTabs(aDelay) {
158 if (!this.tabbarVisible)
159 this.displayTabs();
161 ContextUI.dismissTabsWithDelay(aDelay);
162 },
164 /*
165 * Dismiss tab bar after a delay. Fires context ui events.
166 */
167 dismissTabsWithDelay: function (aDelay) {
168 aDelay = aDelay || kForegroundTabAnimationDelay;
169 this._clearDelayedTimeout();
170 this._lastTimeoutDelay = aDelay;
171 this._hidingId = setTimeout(function () {
172 ContextUI.dismissTabs();
173 }, aDelay);
174 },
176 /*
177 * Display the nav bar.
178 *
179 * @return false if we were already visible, and didn't do anything.
180 */
181 displayNavbar: function () {
182 if (Elements.chromeState.getAttribute("navbar") == "visible") {
183 return false;
184 }
186 Elements.navbar.show();
187 Elements.chromeState.setAttribute("navbar", "visible");
188 ContentAreaObserver.updateContentArea();
189 return true;
190 },
192 // Display the tab tray
193 displayTabs: function () {
194 this._clearDelayedTimeout();
195 this._setIsExpanded(true);
196 },
198 // Dismiss the navbar if visible.
199 dismissNavbar: function dismissNavbar() {
200 if (!BrowserUI.isStartTabVisible) {
201 Elements.autocomplete.closePopup();
202 Elements.navbar.dismiss();
203 Elements.chromeState.removeAttribute("navbar");
204 ContentAreaObserver.updateContentArea();
205 }
206 },
208 // Dismiss the tabstray if visible.
209 dismissTabs: function dimissTabs() {
210 this._clearDelayedTimeout();
211 this._setIsExpanded(false);
212 },
214 // Dismiss the appbar if visible.
215 dismissContextAppbar: function dismissContextAppbar() {
216 Elements.contextappbar.dismiss();
217 },
219 /*******************************************
220 * Internal utils
221 */
223 // tabtray state
224 _setIsExpanded: function _setIsExpanded(aFlag, setSilently) {
225 // if the tray can't be expanded, don't expand it.
226 if (!this.isExpandable || this.tabbarVisible == aFlag)
227 return;
229 if (aFlag)
230 Elements.tray.setAttribute("expanded", "true");
231 else
232 Elements.tray.removeAttribute("expanded");
234 if (!setSilently)
235 this._fire(kContextUITabsShowEvent);
236 },
238 _clearDelayedTimeout: function _clearDelayedTimeout() {
239 if (this._hidingId) {
240 clearTimeout(this._hidingId);
241 this._hidingId = 0;
242 this._delayedHide = false;
243 }
244 },
246 _resetDelayedTimeout: function () {
247 this._hidingId = setTimeout(function () {
248 ContextUI.dismissTabs();
249 }, this._lastTimeoutDelay);
250 },
252 /*******************************************
253 * Events
254 */
256 _onEdgeUIStarted: function(aEvent) {
257 this._hasEdgeSwipeStarted = true;
258 this._clearDelayedTimeout();
259 this.toggleNavUI();
260 },
262 _onEdgeUICanceled: function(aEvent) {
263 this._hasEdgeSwipeStarted = false;
264 this.dismiss();
265 },
267 _onEdgeUICompleted: function(aEvent) {
268 if (this._hasEdgeSwipeStarted) {
269 this._hasEdgeSwipeStarted = false;
270 return;
271 }
273 this._clearDelayedTimeout();
274 this.toggleNavUI();
275 },
277 onDownInput: function onDownInput(aEvent) {
278 if (!this.isVisible) {
279 return;
280 }
282 // Various ui element containers we do not update context ui for.
283 let whitelist = [
284 // Clicks on tab bar elements should not close the tab bar. the tabbar
285 // handles this.
286 Elements.tabs,
287 // Don't let a click on an infobar button dismiss the appbar or navbar.
288 // Note the notification box should always hover above these other two
289 // bars.
290 Browser.getNotificationBox()
291 ];
293 if (whitelist.some(elem => elem.contains(aEvent.target))) {
294 return;
295 }
297 // If a start tab is visible only dismiss the tab bar.
298 if (BrowserUI.isStartTabVisible) {
299 ContextUI.dismissTabs();
300 return;
301 }
303 // content, dismiss anything visible
304 if (aEvent.target.ownerDocument.defaultView.top == getBrowser().contentWindow) {
305 this.dismiss();
306 return;
307 }
309 // dismiss tabs and context app bar if visible
310 this.dismissTabs();
311 this.dismissContextAppbar();
312 },
314 onMouseMove: function (aEvent) {
315 if (this._hidingId) {
316 this._clearDelayedTimeout();
317 this._delayedHide = true;
318 }
319 },
321 onMouseLeave: function (aEvent) {
322 if (this._delayedHide) {
323 this._delayedHide = false;
324 this._resetDelayedTimeout();
325 }
326 },
328 handleEvent: function handleEvent(aEvent) {
329 switch (aEvent.type) {
330 case "URLChanged":
331 // "aEvent.detail" is a boolean value that indicates whether actual URL
332 // has changed ignoring URL fragment changes.
333 if (aEvent.target == Browser.selectedBrowser && aEvent.detail) {
334 this.displayNavbar();
335 }
336 break;
337 case "MozEdgeUIStarted":
338 this._onEdgeUIStarted(aEvent);
339 break;
340 case "MozEdgeUICanceled":
341 this._onEdgeUICanceled(aEvent);
342 break;
343 case "MozEdgeUICompleted":
344 this._onEdgeUICompleted(aEvent);
345 break;
346 case "keypress":
347 if (String.fromCharCode(aEvent.which) == "z" &&
348 aEvent.getModifierState("Win"))
349 this.toggleNavUI();
350 break;
351 case "transitionend":
352 setTimeout(function () {
353 ContentAreaObserver.updateContentArea();
354 }, 0);
355 break;
356 case "KeyboardChanged":
357 this.dismissTabs();
358 break;
359 case "mousedown":
360 if (aEvent.button != 0) {
361 break;
362 }
363 this.onDownInput(aEvent);
364 break;
365 case "mousemove":
366 this.onMouseMove(aEvent);
367 break;
368 case "mouseleave":
369 this.onMouseLeave(aEvent);
370 break;
371 case "touchstart":
372 this.onDownInput(aEvent);
373 break;
374 case "ToolPanelShown":
375 case "ToolPanelHidden":
376 this.dismiss();
377 break;
378 case "AlertActive":
379 case "AlertClose":
380 case "TabSelect":
381 ContentAreaObserver.updateContentArea();
382 break;
383 case "MozFlyoutPanelShowing":
384 if (BrowserUI.isStartTabVisible) {
385 this.dismissTabs();
386 this.dismissContextAppbar();
387 } else {
388 this.dismiss();
389 }
390 break;
391 }
392 },
394 _fire: function (name) {
395 let event = document.createEvent("Events");
396 event.initEvent(name, true, true);
397 window.dispatchEvent(event);
398 }
399 };