' +
'
' +
@@ -733,6 +728,7 @@
'authDialog': window.AppAuthenticationDialog,
'contextmenu': window.BrowserContextMenu,
'childWindowFactory': window.ChildWindowFactory,
+ 'statusbar': window.AppStatusbar
};
AppWindow.prototype.openAnimation = 'enlarge';
@@ -2193,5 +2189,16 @@
}
this.setVisible(false);
};
+
+ /**
+ * Statusbar will bypass touch event to us via this method
+ * @param {Object} evt Touch event object
+ * @param {Number} barHeight The height of the statusbar
+ */
+ AppWindow.prototype.handleStatusbarTouch = function(evt, barHeight) {
+ if (this.statusbar) {
+ this.statusbar.handleStatusbarTouch(evt, barHeight);
+ }
+ };
exports.AppWindow = AppWindow;
}(window));
diff --git a/apps/system/js/attention_window.js b/apps/system/js/attention_window.js
index c0639f1e89cc..d030694911b3 100644
--- a/apps/system/js/attention_window.js
+++ b/apps/system/js/attention_window.js
@@ -90,10 +90,6 @@
this.debug('intance id: ' + this.instanceID);
return '
';
@@ -103,7 +99,8 @@
'transitionController': window.AppTransitionController,
'modalDialog': window.AppModalDialog,
'authDialog': window.AppAuthenticationDialog,
- 'attentionToaster': window.AttentionToaster
+ 'attentionToaster': window.AttentionToaster,
+ 'stautsbar': window.AppStatusbar
};
AttentionWindow.REGISTERED_EVENTS =
diff --git a/apps/system/js/hierarchy_manager.js b/apps/system/js/hierarchy_manager.js
index 72b1f32b3cd2..1e3fee3f693d 100644
--- a/apps/system/js/hierarchy_manager.js
+++ b/apps/system/js/hierarchy_manager.js
@@ -8,6 +8,9 @@
'registerHierarchy',
'unregisterHierarchy'
];
+ HierarchyManager.STATES = [
+ 'getTopMostWindow'
+ ];
BaseModule.create(HierarchyManager, {
name: 'HierarchyManager',
EVENT_PREFIX: 'hierachy',
diff --git a/apps/system/js/homescreen_window.js b/apps/system/js/homescreen_window.js
index d1abf0487476..dd70e2940bd4 100644
--- a/apps/system/js/homescreen_window.js
+++ b/apps/system/js/homescreen_window.js
@@ -70,6 +70,8 @@
HomescreenWindow.prototype = Object.create(AppWindow.prototype);
+ HomescreenWindow.prototype.constructor = HomescreenWindow;
+
HomescreenWindow.prototype._DEBUG = false;
HomescreenWindow.prototype.CLASS_NAME = 'HomescreenWindow';
@@ -102,17 +104,15 @@
this.isHomescreen = true;
};
- HomescreenWindow.REGISTERED_EVENTS =
- ['_opening', '_localized', 'mozbrowserclose', 'mozbrowsererror',
- 'mozbrowservisibilitychange', 'mozbrowserloadend', '_orientationchange',
- '_focus'];
+ HomescreenWindow.REGISTERED_EVENTS = AppWindow.REGISTERED_EVENTS;
HomescreenWindow.SUB_COMPONENTS = {
'transitionController': window.AppTransitionController,
'modalDialog': window.AppModalDialog,
'valueSelector': window.ValueSelector,
'authDialog': window.AppAuthenticationDialog,
- 'childWindowFactory': window.ChildWindowFactory
+ 'childWindowFactory': window.ChildWindowFactory,
+ 'statusbar': window.AppStatusbar
};
HomescreenWindow.prototype.openAnimation = 'zoom-out';
@@ -165,11 +165,6 @@
HomescreenWindow.prototype.view = function hw_view() {
return '
' +
- '
' +
- '
' +
- '
' +
- '
' +
- '
' +
'
' +
'
' +
'
';
diff --git a/apps/system/js/lockscreen_window.js b/apps/system/js/lockscreen_window.js
index 2aade874b137..67c8032df8de 100644
--- a/apps/system/js/lockscreen_window.js
+++ b/apps/system/js/lockscreen_window.js
@@ -44,6 +44,15 @@
*/
LockScreenWindow.prototype = Object.create(AppWindow.prototype);
+ LockScreenWindow.prototype.constructor = LockScreenWindow;
+
+ LockScreenWindow.SUB_COMPONENTS = {
+ 'transitionController': window.AppTransitionController,
+ 'statusbar': window.AppStatusbar
+ };
+
+ LockScreenWindow.REGISTERED_EVENTS = AppWindow.REGISTERED_EVENTS;
+
/**
* We still need this before we put the lockreen inside an iframe.
*
diff --git a/apps/system/js/popup_window.js b/apps/system/js/popup_window.js
index 5afc9e2680d6..673c75cccbb8 100644
--- a/apps/system/js/popup_window.js
+++ b/apps/system/js/popup_window.js
@@ -41,7 +41,8 @@
'valueSelector': window.ValueSelector,
'authDialog': window.AppAuthenticationDialog,
'contextmenu': window.BrowserContextMenu,
- 'childWindowFactory': window.ChildWindowFactory
+ 'childWindowFactory': window.ChildWindowFactory,
+ 'statusbar': window.AppStatusbar
};
/**
diff --git a/apps/system/js/statusbar.js b/apps/system/js/statusbar.js
index caae2d59b249..364965749377 100644
--- a/apps/system/js/statusbar.js
+++ b/apps/system/js/statusbar.js
@@ -14,7 +14,7 @@
limitations under the License.
*/
-/*global Clock, SettingsListener, TouchForwarder, FtuLauncher, MobileOperator,
+/*global Clock, SettingsListener, FtuLauncher, MobileOperator,
SIMSlotManager, Service, Bluetooth, UtilityTray, nfcManager,
layoutManager */
@@ -649,14 +649,6 @@ var StatusBar = {
);
},
- _startX: null,
- _startY: null,
- _releaseTimeout: null,
- _touchStart: null,
- _touchForwarder: new TouchForwarder(),
- _shouldForwardTap: false,
- _dontStopEvent: false,
-
_getMaximizedStatusBarWidth: function sb_getMaximizedStatusBarWidth() {
// Let's consider the style of the status bar:
// * padding: 0 0.3rem;
@@ -777,10 +769,6 @@ var StatusBar = {
},
panelHandler: function sb_panelHandler(evt) {
- var app = Service.currentApp.getTopMostWindow();
- var chromeBar = app.element.querySelector('.chrome');
- var titleBar = app.element.querySelector('.titlebar');
-
// Do not forward events if FTU is running
if (FtuLauncher.isFtuRunning()) {
return;
@@ -791,142 +779,8 @@ var StatusBar = {
return;
}
- if (this._dontStopEvent) {
- return;
- }
-
- // If the app is not a fullscreen app, let utility_tray.js handle
- // this instead.
- if (!document.mozFullScreen && !app.isFullScreen()) {
- return;
- }
-
- evt.stopImmediatePropagation();
- evt.preventDefault();
-
- var touch;
- switch (evt.type) {
- case 'touchstart':
- clearTimeout(this._releaseTimeout);
-
- var iframe = app.iframe;
- this._touchForwarder.destination = iframe;
- this._touchStart = evt;
- this._shouldForwardTap = true;
-
-
- touch = evt.changedTouches[0];
- this._startX = touch.clientX;
- this._startY = touch.clientY;
-
- chromeBar.style.transition = 'transform';
- titleBar.style.transition = 'transform';
- break;
-
- case 'touchmove':
- touch = evt.touches[0];
- var height = this._cacheHeight;
- var deltaX = touch.clientX - this._startX;
- var deltaY = touch.clientY - this._startY;
-
- if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
- this._shouldForwardTap = false;
- }
-
- var translate = Math.min(deltaY, height);
- var heightThreshold = height;
-
- if (app && app.isFullScreen() && app.config.chrome &&
- app.config.chrome.navigation) {
- translate = Math.min(deltaY, app.appChrome.height);
- heightThreshold = app.appChrome.height;
-
- titleBar.style.transform = 'translateY(calc(' +
- (translate - app.appChrome.height) + 'px)';
- } else {
- titleBar.style.transform =
- 'translateY(calc(' + translate + 'px - 100%)';
- }
- chromeBar.style.transform =
- 'translateY(calc(' + translate + 'px - 100%)';
-
- if (translate >= heightThreshold) {
- if (this._touchStart) {
- this._touchForwarder.forward(this._touchStart);
- this._touchStart = null;
- }
- this._touchForwarder.forward(evt);
- }
- break;
-
- case 'touchend':
- clearTimeout(this._releaseTimeout);
-
- if (this._touchStart) {
- if (this._shouldForwardTap) {
- this._touchForwarder.forward(this._touchStart);
- this._touchForwarder.forward(evt);
- this._touchStart = null;
- }
- this._releaseBar(titleBar);
- } else {
- // If we already forwarded the touchstart it means the bar
- // if fully open, releasing after a timeout.
- this._dontStopEvent = true;
- this._touchForwarder.forward(evt);
- this._releaseAfterTimeout(titleBar);
- }
-
- break;
- }
- },
-
- _releaseBar: function sb_releaseBar(titleBar) {
- this._dontStopEvent = false;
- var chromeBar = titleBar.parentNode.querySelector('.chrome');
-
- chromeBar.classList.remove('dragged');
- chromeBar.style.transform = '';
- chromeBar.style.transition = '';
-
- titleBar.classList.remove('dragged');
- titleBar.style.transform = '';
- titleBar.style.transition = '';
-
- this.screen.classList.remove('minimized-tray');
-
- clearTimeout(this._releaseTimeout);
- this._releaseTimeout = null;
- },
-
- _releaseAfterTimeout: function sb_releaseAfterTimeout(titleBar) {
- this.screen.classList.add('minimized-tray');
-
- var chromeBar = titleBar.parentNode.querySelector('.chrome');
-
- var self = this;
- titleBar.style.transform = '';
- titleBar.style.transition = '';
- titleBar.classList.add('dragged');
-
- chromeBar.style.transform = '';
- chromeBar.style.transition = '';
- chromeBar.classList.add('dragged');
-
- self._releaseTimeout = setTimeout(function() {
- self._releaseBar(titleBar);
- window.removeEventListener('touchstart', closeOnTap);
- }, 5000);
-
- function closeOnTap(evt) {
- if (evt.target != self._touchForwarder.destination) {
- return;
- }
-
- window.removeEventListener('touchstart', closeOnTap);
- self._releaseBar(titleBar);
- }
- window.addEventListener('touchstart', closeOnTap);
+ var app = Service.query('getTopMostWindow');
+ app && app.handleStatusbarTouch(evt, this._cacheHeight);
},
/**
diff --git a/apps/system/test/unit/app_statusbar_test.js b/apps/system/test/unit/app_statusbar_test.js
new file mode 100644
index 000000000000..8c07c2701f26
--- /dev/null
+++ b/apps/system/test/unit/app_statusbar_test.js
@@ -0,0 +1,242 @@
+/* global MockAppWindow, AppStatusbar, MocksHelper, MockTouchForwarder */
+'use strict';
+
+require('/shared/test/unit/mocks/mock_l10n.js');
+requireApp('system/test/unit/mock_app_window.js');
+require('/test/unit/mock_touch_forwarder.js');
+requireApp('system/js/service.js');
+requireApp('system/js/base_ui.js');
+requireApp('system/js/app_statusbar.js');
+
+var mocksForAppStatusbar = new MocksHelper([
+ 'AppWindow',
+ 'TouchForwarder'
+]).init();
+
+suite('system/AppStatusbar', function() {
+ mocksForAppStatusbar.attachTestHelpers();
+ var app;
+ var subject;
+ setup(function() {
+ this.sinon.useFakeTimers();
+ app = new MockAppWindow({ chrome: { navigation: false }});
+ subject = new AppStatusbar(app);
+ this.sinon.stub(app, 'isFullScreen').returns(true);
+ subject.screen = document.createElement('div');
+ subject.chromeBar = document.createElement('div');
+ subject.titleBar = document.createElement('div');
+ });
+
+ suite('fullscreen mode >', function() {
+ function forgeTouchEvent(type, x, y) {
+ var touch = document.createTouch(window, null, 42, x, y,
+ x, y, x, y,
+ 0, 0, 0, 0);
+ var touchList = document.createTouchList(touch);
+ var touches = (type == 'touchstart' || type == 'touchmove') ?
+ touchList : null;
+ var changed = (type == 'touchmove') ?
+ null : touchList;
+
+ var e = document.createEvent('TouchEvent');
+ e.initTouchEvent(type, true, true,
+ null, null, false, false, false, false,
+ touches, null, changed);
+
+ return e;
+ }
+
+ function forgeMouseEvent(type, x, y) {
+ var e = document.createEvent('MouseEvent');
+
+ e.initMouseEvent(type, true, true, window, 1, x, y, x, y,
+ false, false, false, false, 0, null);
+
+ return e;
+ }
+
+ function fakeDispatch(type, x, y) {
+ var e;
+ if (type.startsWith('mouse')) {
+ e = forgeMouseEvent(type, x, y);
+ } else {
+ e = forgeTouchEvent(type, x, y);
+ }
+ subject.handleStatusbarTouch(e, 24);
+
+ return e;
+ }
+
+ suite('Revealing the StatusBar >', function() {
+ var transitionEndSpy;
+ var element;
+ setup(function() {
+ element = subject.titleBar;
+ transitionEndSpy = this.sinon.spy(element, 'addEventListener');
+ });
+
+ function assertStatusBarReleased() {
+ assert.isFalse(element.classList.contains('dragged'));
+ }
+
+ teardown(function() {
+ this.sinon.clock.tick(10000);
+ });
+
+ test('it should prevent default on mouse events',
+ function() {
+ var mousedown = fakeDispatch('mousedown', 100, 0);
+ var mousemove = fakeDispatch('mousemove', 100, 2);
+ var mouseup = fakeDispatch('mouseup', 100, 2);
+
+ assert.isTrue(mousedown.defaultPrevented);
+ assert.isTrue(mousemove.defaultPrevented);
+ assert.isTrue(mouseup.defaultPrevented);
+ });
+
+ test('it should prevent default on all touch events to prevent reflows',
+ function() {
+ var touchstart = fakeDispatch('touchstart', 100, 0);
+ var touchmove = fakeDispatch('touchmove', 100, 2);
+ var touchend = fakeDispatch('touchend', 100, 2);
+
+ assert.isTrue(touchstart.defaultPrevented);
+ assert.isTrue(touchmove.defaultPrevented);
+ assert.isTrue(touchend.defaultPrevented);
+ });
+
+ test('it should stop the propagation of the events at first', function() {
+ var fakeEvt = {
+ stopImmediatePropagation: function() {},
+ preventDefault: function() {},
+ type: 'fake'
+ };
+ this.sinon.spy(fakeEvt, 'stopImmediatePropagation');
+ subject.handleStatusbarTouch(fakeEvt, 24);
+ sinon.assert.calledOnce(fakeEvt.stopImmediatePropagation);
+ });
+
+ test('it should translate the statusbar on touchmove', function() {
+ fakeDispatch('touchstart', 100, 0);
+ fakeDispatch('touchmove', 100, 5);
+ var transform = 'translateY(calc(5px - 100%))';
+ assert.equal(element.style.transform, transform);
+ fakeDispatch('touchend', 100, 5);
+ });
+
+ test('it should set the dragged class on touchstart', function() {
+ fakeDispatch('touchstart', 100, 0);
+ assert.isFalse(element.classList.contains('dragged'));
+ fakeDispatch('touchmove', 100, 24);
+ fakeDispatch('touchend', 100, 25);
+ assert.isTrue(element.classList.contains('dragged'));
+ });
+
+ test('it should not translate the statusbar more than its height',
+ function() {
+ fakeDispatch('touchstart', 100, 0);
+ fakeDispatch('touchmove', 100, 5);
+ fakeDispatch('touchmove', 100, 15);
+ var transform = 'translateY(calc(15px - 100%))';
+ assert.equal(element.style.transform, transform);
+ fakeDispatch('touchend', 100, 15);
+ });
+
+ suite('after the gesture', function() {
+ suite('when the StatusBar is not fully displayed', function() {
+ setup(function() {
+ fakeDispatch('touchstart', 100, 0);
+ fakeDispatch('touchmove', 100, 5);
+ fakeDispatch('touchend', 100, 5);
+ });
+
+ test('it should hide it right away', function() {
+ assertStatusBarReleased();
+ });
+ });
+
+ suite('when the StatusBar is fully displayed', function() {
+ setup(function() {
+ fakeDispatch('touchstart', 100, 0);
+ fakeDispatch('touchmove', 100, 5);
+ fakeDispatch('touchmove', 100, 24);
+ fakeDispatch('touchend', 100, 24);
+ });
+
+ test('it should not hide it right away', function() {
+ assert.equal(subject.titleBar.style.transform, '');
+ assert.equal(subject.titleBar.style.transition, '');
+ assert.ok(subject.titleBar.classList.contains('dragged'));
+ });
+
+ test('but after 5 seconds', function() {
+ this.sinon.clock.tick(5000);
+ assertStatusBarReleased();
+ });
+
+ test('or if the user interacts with the app', function() {
+ // We're faking a touchstart event on the app iframe
+ var iframe = app.browser.element;
+ subject._touchForwarder.destination = window;
+
+ var e = forgeTouchEvent('touchstart', 100, 100);
+ window.dispatchEvent(e);
+
+ assertStatusBarReleased();
+ subject._touchForwarder.destination = iframe;
+ });
+ });
+ });
+ });
+
+ suite('Touch forwarding in fullscreen >', function() {
+ var forwardSpy;
+
+ setup(function() {
+ forwardSpy = this.sinon.spy(MockTouchForwarder.prototype, 'forward');
+ });
+
+ test('it should forward taps to the app', function() {
+ var touchstart = fakeDispatch('touchstart', 100, 0);
+ fakeDispatch('touchmove', 100, 2);
+ var touchend = fakeDispatch('touchend', 100, 2);
+
+ assert.isTrue(forwardSpy.calledTwice);
+ var call = forwardSpy.firstCall;
+ assert.equal(call.args[0], touchstart);
+ call = forwardSpy.getCall(1);
+ assert.equal(call.args[0], touchend);
+ });
+
+ suite('if it\'s not a tap and the statusbar is not fully displayed',
+ function() {
+ test('it should not forward any events', function() {
+ fakeDispatch('touchstart', 100, 0);
+ fakeDispatch('touchmove', 100, 8);
+ fakeDispatch('touchend', 100, 8);
+
+ assert.isTrue(forwardSpy.notCalled);
+ });
+ });
+
+ test('it should forward touchmove once the statusbar is shown',
+ function() {
+ var touchstart = fakeDispatch('touchstart', 100, 0);
+ fakeDispatch('touchmove', 100, 6);
+ var secondMove = fakeDispatch('touchmove', 100, 26);
+ var thirdMove = fakeDispatch('touchmove', 100, 28);
+ var touchend = fakeDispatch('touchend', 100, 2);
+
+ assert.equal(forwardSpy.callCount, 4);
+ var call = forwardSpy.firstCall;
+ assert.equal(call.args[0], touchstart);
+ call = forwardSpy.getCall(1);
+ assert.equal(call.args[0], secondMove);
+ call = forwardSpy.getCall(2);
+ assert.equal(call.args[0], thirdMove);
+ call = forwardSpy.getCall(3);
+ assert.equal(call.args[0], touchend);
+ });
+ });
+ });
+});
diff --git a/apps/system/test/unit/app_window_test.js b/apps/system/test/unit/app_window_test.js
index e358470892e3..4be90add66da 100644
--- a/apps/system/test/unit/app_window_test.js
+++ b/apps/system/test/unit/app_window_test.js
@@ -2477,4 +2477,15 @@ suite('system/AppWindow', function() {
appInput.element.dispatchEvent(new CustomEvent('_opened'));
assert.equal(appInput.appChrome, undefined);
});
+
+ test('Should bypass touch event to statusbar submodule', function() {
+ var app = new AppWindow(fakeAppConfig1);
+ app.statusbar = {
+ handleStatusbarTouch: this.sinon.spy()
+ };
+ var fakeTouchEvt = new CustomEvent('touchstart');
+ app.handleStatusbarTouch(fakeTouchEvt, 24);
+ assert.isTrue(app.statusbar.handleStatusbarTouch.calledWith(
+ fakeTouchEvt, 24));
+ });
});
diff --git a/apps/system/test/unit/mock_app_window.js b/apps/system/test/unit/mock_app_window.js
index 24711af8fe98..45539b30714f 100644
--- a/apps/system/test/unit/mock_app_window.js
+++ b/apps/system/test/unit/mock_app_window.js
@@ -10,7 +10,11 @@
var MockAppWindow = function AppWindow(config) {
if (config) {
for (var key in config) {
- this[key] = config[key];
+ try {
+ this[key] = config[key];
+ } catch (e) {
+
+ }
}
this.config = config;
}
@@ -37,6 +41,12 @@
}
return this._element;
},
+ get titleBar() {
+ if (!this._titleBar) {
+ this._titleBar = document.createElement('div');
+ }
+ return this._titleBar;
+ },
get browser() {
if (!this._iframe) {
this._iframe = document.createElement('iframe');
@@ -46,6 +56,16 @@
element: this._iframe
};
},
+ get frame() {
+ return this.element;
+ },
+ get iframe() {
+ if (!this._iframe) {
+ this._iframe = document.createElement('iframe');
+ this._iframe.download = function() {};
+ }
+ return this._iframe;
+ },
render: function() {},
open: function() {},
close: function() {},
@@ -120,7 +140,8 @@
'_resize': function() {},
isForeground: function() {},
killable: function() {},
- setVisibileForScreenReader: function() {}
+ setVisibileForScreenReader: function() {},
+ handleStatusbarTouch: function() {}
};
MockAppWindow.mTeardown = function() {
MockAppWindowHelper.mInstances = [];
diff --git a/apps/system/test/unit/statusbar_test.js b/apps/system/test/unit/statusbar_test.js
index c54dbc54c04d..fa7c7184ca33 100644
--- a/apps/system/test/unit/statusbar_test.js
+++ b/apps/system/test/unit/statusbar_test.js
@@ -1,9 +1,9 @@
/* globals FtuLauncher, MockL10n, MockMobileOperator, MockLayoutManager,
MockNavigatorMozMobileConnections, MockNavigatorMozTelephony,
MockSettingsListener, MocksHelper, MockSIMSlot, MockSIMSlotManager,
- MockService, MockTouchForwarder, StatusBar, Service,
+ MockService, StatusBar, Service,
MockNfcManager, MockMobileconnection, MockAppWindowManager,
- MockNavigatorBattery, UtilityTray */
+ MockNavigatorBattery, UtilityTray, MockAppWindow */
'use strict';
require('/shared/test/unit/mocks/mock_settings_listener.js');
@@ -24,17 +24,18 @@ require('/test/unit/mock_touch_forwarder.js');
require('/test/unit/mock_utility_tray.js');
require('/test/unit/mock_layout_manager.js');
require('/test/unit/mock_navigator_battery.js');
+require('/test/unit/mock_app_window.js');
var mocksForStatusBar = new MocksHelper([
- 'FtuLauncher',
'SettingsListener',
'MobileOperator',
'SIMSlotManager',
- 'AppWindowManager',
- 'TouchForwarder',
'UtilityTray',
'LayoutManager',
- 'NavigatorBattery'
+ 'NavigatorBattery',
+ 'AppWindow',
+ 'Service',
+ 'FtuLauncher'
]).init();
suite('system/Statusbar', function() {
@@ -1505,21 +1506,21 @@ suite('system/Statusbar', function() {
suite('fullscreen mode >', function() {
function forgeTouchEvent(type, x, y) {
- var touch = document.createTouch(window, null, 42, x, y,
- x, y, x, y,
- 0, 0, 0, 0);
- var touchList = document.createTouchList(touch);
- var touches = (type == 'touchstart' || type == 'touchmove') ?
- touchList : null;
- var changed = (type == 'touchmove') ?
- null : touchList;
-
- var e = document.createEvent('TouchEvent');
- e.initTouchEvent(type, true, true,
- null, null, false, false, false, false,
- touches, null, changed);
-
- return e;
+ var touch = document.createTouch(window, null, 42, x, y,
+ x, y, x, y,
+ 0, 0, 0, 0);
+ var touchList = document.createTouchList(touch);
+ var touches = (type == 'touchstart' || type == 'touchmove') ?
+ touchList : null;
+ var changed = (type == 'touchmove') ?
+ null : touchList;
+
+ var e = document.createEvent('TouchEvent');
+ e.initTouchEvent(type, true, true,
+ null, null, false, false, false, false,
+ touches, null, changed);
+
+ return e;
}
function forgeMouseEvent(type, x, y) {
@@ -1545,37 +1546,9 @@ suite('system/Statusbar', function() {
var app;
setup(function() {
- app = {
- isFullScreen: function() {
- return true;
- },
- titleBar: {
- element: document.createElement('div')
- },
- iframe: document.createElement('iframe'),
- getTopMostWindow: function() {
- return app;
- },
- config: {},
- _element: null,
- get element() {
- if (!this._element) {
- var element = document.createElement('div');
- var title = document.createElement('div');
- title.classList.add('titlebar');
- element.appendChild(title);
-
- var chrome = document.createElement('div');
- chrome.className = 'chrome';
- element.appendChild(chrome);
- this._element = element;
- }
-
- return this._element;
- }
- };
-
- Service.currentApp = app;
+ app = new MockAppWindow();
+ MockService.mTopMostWindow = app;
+ this.sinon.stub(app, 'handleStatusbarTouch');
this.sinon.stub(StatusBar.element, 'getBoundingClientRect').returns({
height: 10
});
@@ -1584,62 +1557,34 @@ suite('system/Statusbar', function() {
});
suite('Revealing the StatusBar >', function() {
- var transitionEndSpy;
- var element;
setup(function() {
StatusBar._cacheHeight = 24;
- element = Service.currentApp.element
- .querySelector('.titlebar');
- transitionEndSpy = this.sinon.spy(element, 'addEventListener');
});
- function assertStatusBarReleased() {
- assert.equal(StatusBar.element.style.transform, '');
- assert.equal(StatusBar.element.style.transition, '');
-
- assert.isFalse(element.classList.contains('dragged'));
- }
-
teardown(function() {
this.sinon.clock.tick(10000);
StatusBar.element.style.transition = '';
StatusBar.element.style.transform = '';
});
- test('it should stop the propagation of the events at first', function() {
- var fakeEvt = {
- stopImmediatePropagation: function() {},
- preventDefault: function() {},
- type: 'fake'
- };
- this.sinon.spy(fakeEvt, 'stopImmediatePropagation');
- StatusBar.panelHandler(fakeEvt);
- sinon.assert.calledOnce(fakeEvt.stopImmediatePropagation);
- });
-
test('it should translate the statusbar on touchmove', function() {
fakeDispatch('touchstart', 100, 0);
- fakeDispatch('touchmove', 100, 5);
- var transform = 'translateY(calc(5px - 100%))';
- assert.equal(element.style.transform, transform);
+ var evt = fakeDispatch('touchmove', 100, 5);
+ assert.isTrue(app.handleStatusbarTouch.calledWith(evt, 24));
fakeDispatch('touchend', 100, 5);
});
- test('it should set the dragged class on touchstart', function() {
- fakeDispatch('touchstart', 100, 0);
- assert.isFalse(element.classList.contains('dragged'));
- fakeDispatch('touchmove', 100, 24);
- fakeDispatch('touchend', 100, 25);
- assert.isTrue(element.classList.contains('dragged'));
+ test('it should bypass touchstart event', function() {
+ var evt = fakeDispatch('touchstart', 100, 0);
+ assert.isTrue(app.handleStatusbarTouch.calledWith(evt, 24));
});
test('it should not translate the statusbar more than its height',
function() {
fakeDispatch('touchstart', 100, 0);
fakeDispatch('touchmove', 100, 5);
- fakeDispatch('touchmove', 100, 15);
- var transform = 'translateY(calc(15px - 100%))';
- assert.equal(element.style.transform, transform);
+ var evt = fakeDispatch('touchmove', 100, 15);
+ assert.isTrue(app.handleStatusbarTouch.calledWith(evt, 24));
fakeDispatch('touchend', 100, 15);
});
@@ -1664,10 +1609,8 @@ suite('system/Statusbar', function() {
FtuLauncher.mIsRunning = true;
fakeDispatch('touchstart', 100, 0);
fakeDispatch('touchmove', 100, 100);
-
- var titleEl = Service.currentApp.element
- .querySelector('.titlebar');
- assert.equal(titleEl.style.transform, '');
+
+ assert.isFalse(app.handleStatusbarTouch.called);
FtuLauncher.mIsRunning = false;
});
@@ -1676,140 +1619,9 @@ suite('system/Statusbar', function() {
fakeDispatch('touchstart', 100, 0);
fakeDispatch('touchmove', 100, 100);
- var titleEl = Service.currentApp.element
- .querySelector('.titlebar');
- assert.equal(titleEl.style.transform, '');
+ assert.isFalse(app.handleStatusbarTouch.called);
UtilityTray.active = false;
});
-
-
- suite('after the gesture', function() {
- suite('when the StatusBar is not fully displayed', function() {
- setup(function() {
- fakeDispatch('touchstart', 100, 0);
- fakeDispatch('touchmove', 100, 5);
- fakeDispatch('touchend', 100, 5);
- });
-
- test('it should hide it right away', function() {
- assertStatusBarReleased();
- });
- });
-
- suite('when the StatusBar is fully displayed', function() {
- setup(function() {
- fakeDispatch('touchstart', 100, 0);
- fakeDispatch('touchmove', 100, 5);
- fakeDispatch('touchmove', 100, 24);
- fakeDispatch('touchend', 100, 24);
- });
-
- test('it should not hide it right away', function() {
- var titleEl = Service.currentApp.element
- .querySelector('.titlebar');
- assert.equal(titleEl.style.transform, '');
- assert.equal(titleEl.style.transition, '');
- assert.ok(titleEl.classList.contains('dragged'));
- });
-
- test('but after 5 seconds', function() {
- this.sinon.clock.tick(5000);
- assertStatusBarReleased();
- });
-
- test('or if the user interacts with the app', function() {
- // We're faking a touchstart event on the app iframe
- var iframe = StatusBar._touchForwarder.destination;
- StatusBar._touchForwarder.destination = window;
-
- var e = forgeTouchEvent('touchstart', 100, 100);
- window.dispatchEvent(e);
-
- assertStatusBarReleased();
- StatusBar._touchForwarder.destination = iframe;
- });
- });
- });
- });
-
- test('it should prevent default on mouse events keep the focus on the app',
- function() {
- var mousedown = fakeDispatch('mousedown', 100, 0);
- var mousemove = fakeDispatch('mousemove', 100, 2);
- var mouseup = fakeDispatch('mouseup', 100, 2);
-
- assert.isTrue(mousedown.defaultPrevented);
- assert.isTrue(mousemove.defaultPrevented);
- assert.isTrue(mouseup.defaultPrevented);
- });
-
- suite('Touch forwarding in fullscreen >', function() {
- var forwardSpy;
-
- setup(function() {
- StatusBar._cacheHeight = 24;
- forwardSpy = this.sinon.spy(MockTouchForwarder.prototype, 'forward');
- });
-
- test('it should prevent default on all touch events to prevent reflows',
- function() {
- var touchstart = fakeDispatch('touchstart', 100, 0);
- var touchmove = fakeDispatch('touchmove', 100, 2);
- var touchend = fakeDispatch('touchend', 100, 2);
-
- assert.isTrue(touchstart.defaultPrevented);
- assert.isTrue(touchmove.defaultPrevented);
- assert.isTrue(touchend.defaultPrevented);
- });
-
- test('it should set the destination of the TouchForwarder on touchstart',
- function() {
- fakeDispatch('touchstart', 100, 0);
- assert.equal(StatusBar._touchForwarder.destination, app.iframe);
- fakeDispatch('touchend', 100, 0);
- });
-
- test('it should forward taps to the app', function() {
- var touchstart = fakeDispatch('touchstart', 100, 0);
- fakeDispatch('touchmove', 100, 2);
- var touchend = fakeDispatch('touchend', 100, 2);
-
- assert.isTrue(forwardSpy.calledTwice);
- var call = forwardSpy.firstCall;
- assert.equal(call.args[0], touchstart);
- call = forwardSpy.getCall(1);
- assert.equal(call.args[0], touchend);
- });
-
- suite('if it\'s not a tap and the statusbar is not fully displayed',
- function() {
- test('it should not forward any events', function() {
- fakeDispatch('touchstart', 100, 0);
- fakeDispatch('touchmove', 100, 8);
- fakeDispatch('touchend', 100, 8);
-
- assert.isTrue(forwardSpy.notCalled);
- });
- });
-
- test('it should forward touchmove once the statusbar is shown',
- function() {
- var touchstart = fakeDispatch('touchstart', 100, 0);
- fakeDispatch('touchmove', 100, 6);
- var secondMove = fakeDispatch('touchmove', 100, 26);
- var thirdMove = fakeDispatch('touchmove', 100, 28);
- var touchend = fakeDispatch('touchend', 100, 2);
-
- assert.equal(forwardSpy.callCount, 4);
- var call = forwardSpy.firstCall;
- assert.equal(call.args[0], touchstart);
- call = forwardSpy.getCall(1);
- assert.equal(call.args[0], secondMove);
- call = forwardSpy.getCall(2);
- assert.equal(call.args[0], thirdMove);
- call = forwardSpy.getCall(3);
- assert.equal(call.args[0], touchend);
- });
});
});
diff --git a/apps/system/test/unit/task_manager_test.js b/apps/system/test/unit/task_manager_test.js
index c66532e91c80..deee84459160 100644
--- a/apps/system/test/unit/task_manager_test.js
+++ b/apps/system/test/unit/task_manager_test.js
@@ -115,9 +115,7 @@ suite('system/TaskManager >', function() {
'http://sms.gaiamobile.org': new AppWindow({
launchTime: 5,
name: 'SMS',
- element: document.createElement('div'),
frame: document.createElement('div'),
- iframe: document.createElement('iframe'),
manifest: {
orientation: 'portrait-primary'
},
@@ -137,9 +135,6 @@ suite('system/TaskManager >', function() {
'http://game.gaiamobile.org': new AppWindow({
launchTime: 4,
name: 'GAME',
- element: document.createElement('div'),
- frame: document.createElement('div'),
- iframe: document.createElement('iframe'),
manifest: {
orientation: 'landscape-primary'
},
@@ -156,9 +151,6 @@ suite('system/TaskManager >', function() {
'browser1': new AppWindow({
launchTime: 4,
name: 'BROWSER1',
- element: document.createElement('div'),
- frame: document.createElement('div'),
- iframe: document.createElement('iframe'),
rotatingDegree: 0,
isBrowser: function() {
return true;
@@ -175,9 +167,6 @@ suite('system/TaskManager >', function() {
'http://game2.gaiamobile.org': new AppWindow({
launchTime: 3,
name: 'GAME2',
- element: document.createElement('div'),
- frame: document.createElement('div'),
- iframe: document.createElement('iframe'),
manifest: {
orientation: 'landscape-secondary'
},
@@ -193,9 +182,6 @@ suite('system/TaskManager >', function() {
}),
'browser2': new AppWindow({
name: 'BROWSER2',
- element: document.createElement('div'),
- frame: document.createElement('div'),
- iframe: document.createElement('iframe'),
rotatingDegree: 0,
isBrowser: function() {
return true;
@@ -211,9 +197,6 @@ suite('system/TaskManager >', function() {
}),
'search': new AppWindow({
name: 'search',
- element: document.createElement('div'),
- frame: document.createElement('div'),
- iframe: document.createElement('iframe'),
rotatingDegree: 0,
isBrowser: function() {
return true;
@@ -236,9 +219,6 @@ suite('system/TaskManager >', function() {
instanceID: 'AppWindow-0',
launchTime: 5,
name: 'SMS',
- element: document.createElement('div'),
- frame: document.createElement('div'),
- iframe: document.createElement('iframe'),
manifest: {
orientation: 'portrait-primary'
},
@@ -257,9 +237,6 @@ suite('system/TaskManager >', function() {
instanceID: 'AppWindow-1',
launchTime: 5,
name: 'GAME',
- element: document.createElement('div'),
- frame: document.createElement('div'),
- iframe: document.createElement('iframe'),
manifest: {
orientation: 'portrait-primary'
},
@@ -278,9 +255,6 @@ suite('system/TaskManager >', function() {
instanceID: 'AppWindow-2',
launchTime: 5,
name: 'GAME2',
- element: document.createElement('div'),
- frame: document.createElement('div'),
- iframe: document.createElement('iframe'),
manifest: {
orientation: 'portrait-primary'
},
diff --git a/shared/test/unit/mocks/mock_service.js b/shared/test/unit/mocks/mock_service.js
index b43efae12e3e..c993d5024671 100644
--- a/shared/test/unit/mocks/mock_service.js
+++ b/shared/test/unit/mocks/mock_service.js
@@ -20,6 +20,8 @@ var MockService = {
return this.runningFTU;
case 'isFtuUpgrading':
return this.mUpgrading;
+ case 'getTopMostWindow':
+ return this.mTopMostWindow;
}
return undefined;
},