Commit e982f201 authored by Ali Juma's avatar Ali Juma Committed by Commit Bot

[iOS] Don't throw un-spec'd exceptions in pushState and replaceState

The injected implementations of pushState and replaceState stringify
the given state using JSON.stringify. This behavior is non-standard,
and leaks to the web when JSON.stringify fails and throws a TypeError.
This happens when the given state object has a cycle.

To prevent leaking this implementation detail, this CL catches exceptions
thrown by JSON.stringify, and then throws a standard DataCloneError instead.

This fixes subtests in the following two Web Platform Tests that currently
fail in Chrome but pass in Safari:
html/browsers/histoy/the-history-interface/001.html
html/browsers/histoy/the-history-interface/002.html

Longer term, we should remove the stringification all together. This is
blocked on deleting legacy navigation and finding an alternate solution to
crbug.com/949305 that doesn't involve capturing and replaying the
state passed to pushState or replaceState.

Bug: 769945
Change-Id: I945251ffe09ecac79f086ca75030dc366ef99286
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1866870Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Commit-Queue: Ali Juma <ajuma@chromium.org>
Cr-Commit-Position: refs/heads/master@{#707022}
parent 52725075
......@@ -63,6 +63,14 @@ __gCrWeb['replaceWebViewURL'] = function(url, stateObject) {
originalWindowHistoryReplaceState.call(history, stateObject, '', url);
};
function DataCloneError() {
// The name and code for this error are defined by the WebIDL spec. See
// https://heycam.github.io/webidl/#datacloneerror
this.name = 'DataCloneError';
this.code = 25;
this.message = "Cyclic structures are not supported.";
}
/**
* Intercepts window.history methods so native code can differentiate between
* same-document navigation that are state navigations vs. hash navigations.
......@@ -73,10 +81,18 @@ __gCrWeb['replaceWebViewURL'] = function(url, stateObject) {
*/
window.history.pushState = function(stateObject, pageTitle, pageUrl) {
__gCrWeb.message.invokeOnHost({'command': 'navigation.willChangeState'});
// Calling stringify() on undefined causes a JSON parse error.
var serializedState = typeof (stateObject) == 'undefined' ?
'' :
__gCrWeb.common.JSONStringify(stateObject);
// JSONStringify throws an exception when given a cyclical object. This
// internal implementation detail should not be exposed to callers of
// pushState. Instead, throw a standard exception when stringification fails.
try {
// Calling stringify() on undefined causes a JSON parse error.
var serializedState = typeof (stateObject) == 'undefined' ?
'' :
__gCrWeb.common.JSONStringify(stateObject);
} catch (e) {
throw new DataCloneError();
}
pageUrl = pageUrl || window.location.href;
originalWindowHistoryPushState.call(history, stateObject, pageTitle, pageUrl);
__gCrWeb.message.invokeOnHost({
......@@ -90,10 +106,18 @@ window.history.pushState = function(stateObject, pageTitle, pageUrl) {
window.history.replaceState = function(stateObject, pageTitle, pageUrl) {
__gCrWeb.message.invokeOnHost({'command': 'navigation.willChangeState'});
// Calling stringify() on undefined causes a JSON parse error.
var serializedState = typeof (stateObject) == 'undefined' ?
'' :
__gCrWeb.common.JSONStringify(stateObject);
// JSONStringify throws an exception when given a cyclical object. This
// internal implementation detail should not be exposed to callers of
// replaceState. Instead, throw a standard exception when stringification
// fails.
try {
// Calling stringify() on undefined causes a JSON parse error.
var serializedState = typeof (stateObject) == 'undefined' ?
'' :
__gCrWeb.common.JSONStringify(stateObject);
} catch (e) {
throw new DataCloneError();
}
pageUrl = pageUrl || window.location.href;
originalWindowHistoryReplaceState.call(
history, stateObject, pageTitle, pageUrl);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment