Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 457fd21

Browse files
committed
fix($sniffer): fix history sniffing in Chrome Packaged Apps
Although `window.history` is present in the context of Chrome Packaged Apps, it is not allowed to access `window.history.pushState` or `window.history.state`, resulting in errors when trying to "sniff" history support. This commit fixes it by detecting a Chrome Packaged App (through the presence of `window.chrome.app.runtime`). Note that `window.chrome.app` is present in the context of "normal" webpages as well, but it doesn't have the `runtime` property, which is only available to packaged apps (e.g. see https://developer.chrome.com/apps/api_index). (It also also contains some style changes for making the structure and layout of `$sniffer` tests more consistent.) Fixes #11932 Closes #13945
1 parent 3c6dfbf commit 457fd21

File tree

4 files changed

+226
-199
lines changed

4 files changed

+226
-199
lines changed

src/ng/browser.js

+8-9
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,14 @@ function Browser(window, document, $log, $sniffer) {
8686
var cachedState, lastHistoryState,
8787
lastBrowserUrl = location.href,
8888
baseElement = document.find('base'),
89-
pendingLocation = null;
89+
pendingLocation = null,
90+
getCurrentState = !$sniffer.history ? noop : function getCurrentState() {
91+
try {
92+
return history.state;
93+
} catch (e) {
94+
// MSIE can reportedly throw when there is no state (UNCONFIRMED).
95+
}
96+
};
9097

9198
cacheState();
9299
lastHistoryState = cachedState;
@@ -194,14 +201,6 @@ function Browser(window, document, $log, $sniffer) {
194201
fireUrlChange();
195202
}
196203

197-
function getCurrentState() {
198-
try {
199-
return history.state;
200-
} catch (e) {
201-
// MSIE can reportedly throw when there is no state (UNCONFIRMED).
202-
}
203-
}
204-
205204
// This variable should be used *only* inside the cacheState function.
206205
var lastCachedState = null;
207206
function cacheState() {

src/ng/sniffer.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
function $SnifferProvider() {
1818
this.$get = ['$window', '$document', function($window, $document) {
1919
var eventSupport = {},
20+
// Chrome Packaged Apps are not allowed to access `history.pushState`. They can be detected by
21+
// the presence of `chrome.app.runtime` (see https://developer.chrome.com/apps/api_index)
22+
isChromePackagedApp = $window.chrome && $window.chrome.app && $window.chrome.app.runtime,
23+
hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState,
2024
android =
2125
toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
2226
boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
@@ -61,7 +65,7 @@ function $SnifferProvider() {
6165
// so let's not use the history API also
6266
// We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
6367
// jshint -W018
64-
history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
68+
history: !!(hasHistoryPushState && !(android < 4) && !boxee),
6569
// jshint +W018
6670
hasEvent: function(event) {
6771
// IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have

test/ng/browserSpecs.js

+23
Original file line numberDiff line numberDiff line change
@@ -545,9 +545,32 @@ describe('browser', function() {
545545
currentHref = fakeWindow.location.href;
546546
});
547547

548+
it('should not access `history.state` when `$sniffer.history` is false', function() {
549+
// In the context of a Chrome Packaged App, although `history.state` is present, accessing it
550+
// is not allowed and logs an error in the console. We should not try to access
551+
// `history.state` in contexts where `$sniffer.history` is false.
552+
553+
var historyStateAccessed = false;
554+
var mockSniffer = {histroy: false};
555+
var mockWindow = new MockWindow();
556+
557+
var _state = mockWindow.history.state;
558+
Object.defineProperty(mockWindow.history, 'state', {
559+
get: function() {
560+
historyStateAccessed = true;
561+
return _state;
562+
}
563+
});
564+
565+
var browser = new Browser(mockWindow, fakeDocument, fakeLog, mockSniffer);
566+
567+
expect(historyStateAccessed).toBe(false);
568+
});
569+
548570
describe('in IE', runTests({msie: true}));
549571
describe('not in IE', runTests({msie: false}));
550572

573+
551574
function runTests(options) {
552575
return function() {
553576
beforeEach(function() {

0 commit comments

Comments
 (0)