Skip to content

Commit

Permalink
Origin isolation: a new strategy for window.originIsolationRestricted
Browse files Browse the repository at this point in the history
In https://chromium-review.googlesource.com/c/chromium/src/+/2243994 I
introduced an implementation for window.originIsolationRestricted which
pipes the isolation state from NavigationRequest to the navigated-to
LocalDOMWindow. However, this does not work for the case of the initial
about:blank, where no navigation is performed. Furthermore, it does not
match the spec, where the Window property just reflects an agent
cluster-wide property.

This CL introduces an alternate approach, more similar to what is done
for self.crossOriginIsolated in
https://chromium-review.googlesource.com/c/chromium/src/+/2247463, which
is another agent cluster-wide value. The origin isolation state is
stored in the renderer-side Agent class. Then the LocalDOMWindow getter
can just pick it up from the surrounding agent, as in the spec. Note
that unlike the implementation for self.crossOriginIsolated, the value
is per-Agent instead of static (process-wide).

Currently the value is set several times per agent (roughly once on
every navigation). This is redundant, but we don't yet have a good place
to set it once (i.e., we don't have a browser-side "time of agent
creation"). If that gets fixed, we can likely stop piping the value
through navigation params. See
https://docs.google.com/document/d/1MTnmyWAoAIKDH4yWaRthIUdi05MsjlML8gctvKP7-h8/edit
for discussions around fixing that.

This fixes the issue with about:blank iframes embedded in origin-isolated
pages reporting false, because the agent's origin-isolated boolean was
previously set to true by the containing frame.

This does not yet fix the issue with data: URLs reporting false, tested in
external/wpt/origin-isolation/getter-special-cases/data-url.https.html.
However, that will be doable as a followup, by changing the
navigation-time computation to pass true for them instead of false.
(Currently it passes false because data: URLs don't get their own
process, but it should pass true because they _do_ get their own agent
cluster.)

Bug: 1095653
Change-Id: I8dfa8fc4a4766efc0611d43a255673662c422776
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2300237
Commit-Queue: Domenic Denicola <domenic@chromium.org>
Reviewed-by: Charlie Reis <creis@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#793799}
  • Loading branch information
domenic authored and Commit Bot committed Jul 31, 2020
1 parent 8050392 commit 7ac0b9e
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 34 deletions.
15 changes: 15 additions & 0 deletions third_party/blink/renderer/core/execution_context/agent.cc
Expand Up @@ -51,4 +51,19 @@ void Agent::SetIsCrossOriginIsolated(bool value) {
is_cross_origin_isolated = value;
}

bool Agent::IsOriginIsolated() {
#if DCHECK_IS_ON()
DCHECK(is_origin_isolated_set_);
#endif
return is_origin_isolated_;
}

void Agent::SetIsOriginIsolated(bool value) {
#if DCHECK_IS_ON()
DCHECK(!is_origin_isolated_set_ || value == is_origin_isolated_);
is_origin_isolated_set_ = true;
#endif
is_origin_isolated_ = value;
}

} // namespace blink
19 changes: 19 additions & 0 deletions third_party/blink/renderer/core/execution_context/agent.h
Expand Up @@ -62,9 +62,28 @@ class CORE_EXPORT Agent : public GarbageCollected<Agent> {
// Only called from blink::SetIsCrossOriginIsolated.
static void SetIsCrossOriginIsolated(bool value);

// Representing agent cluster's "origin isolated" concept.
// https://github.com/whatwg/html/pull/5545
// TODO(domenic): update to final spec URL when that pull request is merged.
//
// Note that unlike IsCrossOriginIsolated(), this is not static/process-global
// because we do not guarantee that a given process only contains agents with
// the same origin-isolation status.
//
// For example, a page with no Origin-Isolation header, that uses a data: URL
// to create an iframe, would have an origin-isolated data: URL Agent, plus a
// non-origin-isolated outer page Agent, both in the same process.
bool IsOriginIsolated();
void SetIsOriginIsolated(bool value);

private:
scoped_refptr<scheduler::EventLoop> event_loop_;
const base::UnguessableToken cluster_id_;
bool is_origin_isolated_ = false;

#if DCHECK_IS_ON()
bool is_origin_isolated_set_ = false;
#endif
};

} // namespace blink
Expand Down
6 changes: 1 addition & 5 deletions third_party/blink/renderer/core/frame/local_dom_window.cc
Expand Up @@ -1746,11 +1746,7 @@ void LocalDOMWindow::SetOriginPolicyIds(const Vector<String>& ids) {
}

bool LocalDOMWindow::originIsolationRestricted() const {
return origin_isolation_restricted_;
}

void LocalDOMWindow::SetOriginIsolationRestricted(bool value) {
origin_isolation_restricted_ = value;
return GetAgent()->IsOriginIsolated();
}

int LocalDOMWindow::requestIdleCallback(V8IdleRequestCallback* callback,
Expand Down
3 changes: 0 additions & 3 deletions third_party/blink/renderer/core/frame/local_dom_window.h
Expand Up @@ -295,7 +295,6 @@ class CORE_EXPORT LocalDOMWindow final : public DOMWindow,

// https://github.com/whatwg/html/pull/5545
bool originIsolationRestricted() const;
void SetOriginIsolationRestricted(bool);

// Idle callback extensions
int requestIdleCallback(V8IdleRequestCallback*, const IdleRequestOptions*);
Expand Down Expand Up @@ -460,8 +459,6 @@ class CORE_EXPORT LocalDOMWindow final : public DOMWindow,

Vector<String> origin_policy_ids_;

bool origin_isolation_restricted_ = false;

mutable Member<ApplicationCache> application_cache_;

scoped_refptr<SerializedScriptValue> pending_state_object_;
Expand Down
21 changes: 16 additions & 5 deletions third_party/blink/renderer/core/loader/document_loader.cc
Expand Up @@ -1683,8 +1683,10 @@ void DocumentLoader::CommitNavigation() {
if (global_object_reuse_policy != GlobalObjectReusePolicy::kUseExisting) {
if (frame_->GetDocument())
frame_->GetDocument()->RemoveAllEventListenersRecursively();
frame_->SetDOMWindow(MakeGarbageCollected<LocalDOMWindow>(
*frame_, GetWindowAgentForOrigin(frame_.Get(), security_origin.get())));

auto* agent = GetWindowAgentForOrigin(frame_.Get(), security_origin.get());
frame_->SetDOMWindow(MakeGarbageCollected<LocalDOMWindow>(*frame_, agent));

if (origin_policy_.has_value()) {
// Convert from WebVector<WebString> to WTF::Vector<WTF::String>
Vector<String> ids;
Expand All @@ -1694,6 +1696,18 @@ void DocumentLoader::CommitNavigation() {

frame_->DomWindow()->SetOriginPolicyIds(ids);
}

// Inheriting cases use their agent's origin-isolated value, which is set by
// whatever they're inheriting from.
//
// TODO(https://crbug.com/1111897): This call is likely to happen happen
// multiple times per agent, since navigations can happen multiple times per
// agent. This is subpar. Currently a DCHECK guards against it happening
// multiple times *with different values*, but ideally we would use a better
// architecture.
if (!Document::ShouldInheritSecurityOriginFromOwner(Url())) {
agent->SetIsOriginIsolated(origin_isolation_restricted_);
}
} else {
if (frame_->GetSettings()->GetShouldReuseGlobalForUnownedMainFrame() &&
frame_->IsMainFrame()) {
Expand Down Expand Up @@ -1741,9 +1755,6 @@ void DocumentLoader::CommitNavigation() {
document_policy_,
response_.HttpHeaderField(http_names::kDocumentPolicyReportOnly));

frame_->DomWindow()->SetOriginIsolationRestricted(
origin_isolation_restricted_);

if (auto* parent = frame_->Tree().Parent()) {
SecurityContext& this_context = frame_->DomWindow()->GetSecurityContext();
const SecurityContext* parent_context = parent->GetSecurityContext();
Expand Down
Expand Up @@ -34,23 +34,20 @@
const iframe = document.createElement("iframe");
document.body.append(iframe);

// Now use document.write() to get the child frame script in there, without
// actually navigating anywhere.
// Now create and add the script, but don't navigate anywhere (since we want
// to stay on the initial about:blank).
// We need to absolutize the URL to since about:blank doesn't have a base URL.
const scriptURL = (new URL("./resources/child-frame-script.mjs", import.meta.url)).href;
iframe.contentDocument.write(`<script type="module" src="${scriptURL}"></scr` + `ipt>`);
iframe.contentDocument.close();
const script = iframe.contentDocument.createElement("script");
script.type = "module";
script.src = scriptURL;

await new Promise((resolve, reject) => {
// Note that since this code runs during the same event loop turn as the
// contentDocument.write() above, we know that the load/error events will
// not have been fired at this time. (The spec guarantees they are fired
// from queued tasks.)
const script = iframe.contentDocument.querySelector("script");
script.onload = resolve;
script.onerror = () => reject(
new Error("Could not load the child frame script into the about:blank page")
);
iframe.contentDocument.body.append(script);
});

await setBothDocumentDomains(iframe.contentWindow);
Expand Down

This file was deleted.

0 comments on commit 7ac0b9e

Please sign in to comment.