Skip to content

Commit

Permalink
AppHistory ongoing navigation tracking
Browse files Browse the repository at this point in the history
Change-Id: I1cb78990a52a10a44325734eed5736507564ebb9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3044718
Commit-Queue: Nate Chapin <japhet@chromium.org>
Reviewed-by: Domenic Denicola <domenic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#909066}
  • Loading branch information
natechapin authored and Chromium LUCI CQ committed Aug 5, 2021
1 parent f27ebe5 commit a143b9b
Show file tree
Hide file tree
Showing 11 changed files with 303 additions and 145 deletions.
286 changes: 173 additions & 113 deletions third_party/blink/renderer/core/app_history/app_history.cc

Large diffs are not rendered by default.

24 changes: 11 additions & 13 deletions third_party/blink/renderer/core/app_history/app_history.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
namespace blink {

class AbortSignal;
class AppHistoryApiNavigation;
class AppHistoryEntry;
class AppHistoryNavigateEvent;
class AppHistoryNavigateOptions;
Expand All @@ -25,7 +26,6 @@ class HTMLFormElement;
class HistoryItem;
class KURL;
class ScriptPromise;
class ScriptPromiseResolver;
class SerializedScriptValue;

// TODO(japhet): This should probably move to frame_loader_types.h and possibly
Expand Down Expand Up @@ -88,7 +88,7 @@ class CORE_EXPORT AppHistory final : public EventTargetWithInlineData,
UserNavigationInvolvement,
SerializedScriptValue* = nullptr,
HistoryItem* destination_item = nullptr);
void CancelOngoingNavigateEvent();
void InformAboutCanceledNavigation();

int GetIndexFor(AppHistoryEntry*);

Expand All @@ -101,25 +101,23 @@ class CORE_EXPORT AppHistory final : public EventTargetWithInlineData,
void Trace(Visitor*) const final;

private:
friend class NavigateReaction;
void PopulateKeySet();
void FinalizeWithAbortedNavigationError(ScriptState*, AbortSignal*);
void FinalizeWithAbortedNavigationError(ScriptState*,
AppHistoryApiNavigation*);

HeapVector<Member<AppHistoryEntry>> entries_;
HashMap<String, int> keys_to_indices_;
int current_index_ = -1;

Member<AppHistoryNavigateEvent> ongoing_navigate_event_;
Member<ScriptPromiseResolver> navigate_method_call_promise_resolver_;
scoped_refptr<SerializedScriptValue> navigate_serialized_state_;

bool did_react_to_promise_ = false;
Member<ScriptPromiseResolver> goto_promise_resolver_;

ScriptValue navigate_event_info_;
ScriptValue goto_navigate_event_info_;
int64_t goto_item_sequence_number_ = 0;
Member<AppHistoryApiNavigation> ongoing_non_traversal_navigation_;
HeapHashMap<String, Member<AppHistoryApiNavigation>> ongoing_traversals_;
Member<AppHistoryApiNavigation> upcoming_non_traversal_navigation_;

Member<AppHistoryNavigateEvent> ongoing_navigate_event_;
Member<AbortSignal> post_navigate_event_ongoing_navigation_signal_;

scoped_refptr<SerializedScriptValue> to_be_set_serialized_state_;
};

} // namespace blink
Expand Down
2 changes: 1 addition & 1 deletion third_party/blink/renderer/core/loader/frame_loader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1092,7 +1092,7 @@ void FrameLoader::StopAllLoaders(bool abort_client) {

frame_->GetDocument()->CancelParsing();
if (auto* app_history = AppHistory::appHistory(*frame_->DomWindow()))
app_history->CancelOngoingNavigateEvent();
app_history->InformAboutCanceledNavigation();
if (document_loader_)
document_loader_->StopLoading();
if (abort_client)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="i"></iframe>
<iframe id="i" src="/common/blank.html"></iframe>
<script>
promise_test(async t => {
await new Promise(resolve => i.onload = resolve);
let iframe_constructor = i.contentWindow.DOMException;
let iframe_typeerror = i.contentWindow.TypeError;
let abort_signal;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,34 @@
<script>
async_test(t => {
window.onload = t.step_func(() => {
let target_url = location.href + "?1";
let abort_signal;
let onabort_called = false;
let navigateErrorException;
appHistory.onnavigateerror = t.step_func(e => {
assert_equals(e.constructor, ErrorEvent);
navigateErrorException = e.error;
assert_equals(e.filename, target_url);
assert_equals(e.lineno, 0);
assert_equals(e.colno, 0);
});
appHistory.onnavigatesuccess = t.unreached_func("onnavigatesuccess");
appHistory.onnavigate = t.step_func(e => {
abort_signal = e.signal;
abort_signal.onabort = () => onabort_called = true;
e.respondWith(new Promise(resolve => t.step_timeout(resolve, 0)));
});
appHistory.navigate("?1");
let navigate_promise = appHistory.navigate(target_url);
window.stop();
assert_true(abort_signal.aborted);
assert_true(onabort_called);
// Complete the test asynchronously to ensure that onnavigatesuccess
// didn't fire on a microtask.
t.step_timeout(t.step_func_done(() => {}), 5);
promise_rejects_dom(t, 'AbortError', navigate_promise).then(() => {
return navigate_promise.catch(e => assert_equals(e, navigateErrorException));
}).then(() => {
// Complete the test asynchronously to ensure that onnavigatesuccess
// didn't fire on a microtask.
t.step_timeout(t.step_func_done(() => {}), 5);
});
});
}, "window.stop() cancels AppHistoryNavigateEvent.respondWith and signals AppHistoryNavigateEvent.signal");
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
async_test(t => {
promise_test(async t => {
// Wait for after the load event so that the navigation doesn't get converted
// into a replace navigation.
window.onload = () => t.step_timeout(async () => {
let key = appHistory.current.key;
await appHistory.navigate("#1");
await new Promise(resolve => window.onload = () => t.step_timeout(resolve, 0));
let key = appHistory.current.key;
await appHistory.navigate("#1");

let first_promise = appHistory.goTo(key);
appHistory.goTo(key);
await promise_rejects_dom(t, 'AbortError', first_promise);
t.done();
}, 0);
let first_promise = appHistory.goTo(key);
let second_promise = appHistory.goTo(key);
assert_equals(first_promise, second_promise);

let second_promise_resolved = false;
second_promise.then(() => second_promise_resolved = true);
await first_promise;
assert_true(second_promise_resolved);
}, "Repeated navigate.goTo()");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="i" src="/common/blank.html"></iframe>
<script>
promise_test(async t => {
await new Promise(resolve => window.onload = resolve);
let navigate_event_count = 0;
i.contentWindow.appHistory.onnavigate = () => navigate_event_count++;
i.contentWindow.appHistory.onnavigatesuccess = t.unreached_func('onnavigatesuccess should not be called');
i.contentWindow.appHistory.onnavigateerror = t.unreached_func('onnavigateerror should not be called');
let onbeforeunload_promise;
// The iframe does not have sticky activation, so per
// https://html.spec.whatwg.org/#prompt-to-unload-a-document, no prompt is
// shown and the navigation will proceed.
i.contentWindow.onbeforeunload = t.step_func(() => {
onbeforeunload_promise = i.contentWindow.appHistory.navigate("#").catch(e => {
assert_equals(e.constructor, i.contentWindow.DOMException);
assert_equals(e.name, "InvalidStateError");
});
});
i.contentWindow.appHistory.navigate("?1");
await onbeforeunload_promise;
assert_equals(navigate_event_count, 1);
}, "Navigate inside onbeforeunload");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/get-host-info.js"></script>
<body>
<script>
promise_test(async t => {
let i = document.createElement("iframe");
document.body.appendChild(i);
i.contentWindow.appHistory.onnavigate = t.unreached_func('onnavigate should not be called');
i.contentWindow.appHistory.onnavigatesuccess = t.unreached_func('onnavigatesuccess should not be called');
i.contentWindow.appHistory.onnavigateerror = t.unreached_func('onnavigateerror should not be called');
let promise_resolved = false;
i.contentWindow.appHistory.navigate(new URL("/common/blank.html?1", location.href).href)
.then(() => promise_resolved = true);
await new Promise(resolve => i.onload = resolve);
assert_equals(i.contentWindow.location.search, "?1");
assert_false(promise_resolved);
}, "navigate in initial about:blank document");
</script>
</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script>
promise_test(async t => {
let i = document.createElement("iframe");
document.body.appendChild(i);
let navigate_event_count = 0;
i.contentWindow.appHistory.onnavigate = t.unreached_func('onnavigate should not be called');
i.contentWindow.appHistory.onnavigatesuccess = t.unreached_func('onnavigatesuccess should not be called');
i.contentWindow.appHistory.onnavigateerror = t.unreached_func('onnavigateerror should not be called');
await i.contentWindow.appHistory.navigate("#");
}, "navigate in initial about:blank document");
</script>
</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="i" src="/common/blank.html"></iframe>
<script>
promise_test(async t => {
await new Promise(resolve => window.onload = resolve);
let navigate_event_count = 0;
i.contentWindow.appHistory.onnavigate = () => navigate_event_count++;
i.contentWindow.appHistory.onnavigatesuccess = t.unreached_func('onnavigatesuccess should not be called');
i.contentWindow.appHistory.onnavigateerror = t.unreached_func('onnavigateerror should not be called');
i.contentWindow.appHistory.navigate("?1");
await new Promise(resolve => {
i.contentWindow.onunload = t.step_func(() => {
i.contentWindow.appHistory.navigate("#").catch(e => {
assert_equals(e.constructor, i.contentWindow.DOMException);
assert_equals(e.name, "InvalidStateError");
resolve();
});
});
});
assert_equals(navigate_event_count, 1);
}, "Navigate inside onunload");
</script>

0 comments on commit a143b9b

Please sign in to comment.