Commit cc86cf8b authored by Jason Chase's avatar Jason Chase Committed by Commit Bot

Add support for origin trials to headless

In crbug.com/1049126, we discovered that origin trials are not enabled
when running in headless mode. This was not intentional, as headless
should behave the same as regular Chrome with respect to origin trials.

The root cause is that origin trials require the embedder (i.e. Chrome)
to explicitly opt-in to enable trials. The opt-in is done by providing
an implementation of OriginTrialPolicy [1]. It turns out that headless
is actually a separate embedder than Chrome, which was missed in the
initial implementation.

A workaround for the problem has been landed in [2], and merged back to
M80. This CL is intended to be merged back to M81.

This CL implements the opt-in for the headless embedder, by copying the
Chrome embedder implementation. Ideally, headless should re-use the
Chrome implementation, but that would require larger changes. The
duplication will be addressed in M82 in crbug.com/1049317.

[1] https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/public/common/origin_trials/origin_trial_policy.h
[2] https://chromium-review.googlesource.com/c/chromium/src/+/2039601

Bug: 1049126
Change-Id: Ide596c100baae4b69eab2149169eb92353a7bac7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2042382
Commit-Queue: Jason Chase <chasej@chromium.org>
Reviewed-by: default avatarMason Freed <masonfreed@chromium.org>
Reviewed-by: default avatarAndrey Kosyakov <caseq@chromium.org>
Reviewed-by: default avatarJohannes Henkel <johannes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#739253}
parent e8b29536
...@@ -251,6 +251,8 @@ jumbo_source_set("headless_shared_sources") { ...@@ -251,6 +251,8 @@ jumbo_source_set("headless_shared_sources") {
"app/headless_shell_switches.h", "app/headless_shell_switches.h",
"lib/headless_content_client.cc", "lib/headless_content_client.cc",
"lib/headless_content_client.h", "lib/headless_content_client.h",
"lib/headless_origin_trial_policy.cc",
"lib/headless_origin_trial_policy.h",
"public/headless_browser.cc", "public/headless_browser.cc",
"public/headless_browser.h", "public/headless_browser.h",
"public/headless_export.h", "public/headless_export.h",
......
...@@ -2,6 +2,9 @@ specific_include_rules = { ...@@ -2,6 +2,9 @@ specific_include_rules = {
"headless_browser_browsertest.cc": [ "headless_browser_browsertest.cc": [
"+third_party/crashpad/crashpad/client", "+third_party/crashpad/crashpad/client",
], ],
"headless_content_client.h|headless_origin_trial_policy.h": [
"+third_party/blink/public/common/origin_trials/origin_trial_policy.h",
],
"headless_content_main_delegate.cc": [ "headless_content_main_delegate.cc": [
"+cc/base/switches.h", "+cc/base/switches.h",
"+components/viz/common/switches.h", "+components/viz/common/switches.h",
...@@ -17,4 +20,3 @@ specific_include_rules = { ...@@ -17,4 +20,3 @@ specific_include_rules = {
"+third_party/skia/include", "+third_party/skia/include",
] ]
} }
...@@ -35,4 +35,14 @@ gfx::Image& HeadlessContentClient::GetNativeImageNamed(int resource_id) { ...@@ -35,4 +35,14 @@ gfx::Image& HeadlessContentClient::GetNativeImageNamed(int resource_id) {
resource_id); resource_id);
} }
blink::OriginTrialPolicy* HeadlessContentClient::GetOriginTrialPolicy() {
// Prevent initialization race (see crbug.com/721144). There may be a
// race when the policy is needed for worker startup (which happens on a
// separate worker thread).
base::AutoLock auto_lock(origin_trial_policy_lock_);
if (!origin_trial_policy_)
origin_trial_policy_ = std::make_unique<HeadlessOriginTrialPolicy>();
return origin_trial_policy_.get();
}
} // namespace headless } // namespace headless
...@@ -5,7 +5,11 @@ ...@@ -5,7 +5,11 @@
#ifndef HEADLESS_LIB_HEADLESS_CONTENT_CLIENT_H_ #ifndef HEADLESS_LIB_HEADLESS_CONTENT_CLIENT_H_
#define HEADLESS_LIB_HEADLESS_CONTENT_CLIENT_H_ #define HEADLESS_LIB_HEADLESS_CONTENT_CLIENT_H_
#include <memory>
#include "base/synchronization/lock.h"
#include "content/public/common/content_client.h" #include "content/public/common/content_client.h"
#include "headless/lib/headless_origin_trial_policy.h"
namespace headless { namespace headless {
...@@ -20,8 +24,13 @@ class HeadlessContentClient : public content::ContentClient { ...@@ -20,8 +24,13 @@ class HeadlessContentClient : public content::ContentClient {
ui::ScaleFactor scale_factor) override; ui::ScaleFactor scale_factor) override;
base::RefCountedMemory* GetDataResourceBytes(int resource_id) override; base::RefCountedMemory* GetDataResourceBytes(int resource_id) override;
gfx::Image& GetNativeImageNamed(int resource_id) override; gfx::Image& GetNativeImageNamed(int resource_id) override;
blink::OriginTrialPolicy* GetOriginTrialPolicy() override;
private: private:
// Used to lock when |origin_trial_policy_| is initialized.
base::Lock origin_trial_policy_lock_;
std::unique_ptr<HeadlessOriginTrialPolicy> origin_trial_policy_;
DISALLOW_COPY_AND_ASSIGN(HeadlessContentClient); DISALLOW_COPY_AND_ASSIGN(HeadlessContentClient);
}; };
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "headless/lib/headless_origin_trial_policy.h"
#include <stdint.h>
#include <vector>
#include "base/base64.h"
#include "base/command_line.h"
#include "base/stl_util.h"
#include "base/strings/string_split.h"
#include "content/public/common/origin_util.h"
// This is the default public key used for validating signatures.
static const uint8_t kDefaultPublicKey[] = {
0x7c, 0xc4, 0xb8, 0x9a, 0x93, 0xba, 0x6e, 0xe2, 0xd0, 0xfd, 0x03,
0x1d, 0xfb, 0x32, 0x66, 0xc7, 0x3b, 0x72, 0xfd, 0x54, 0x3a, 0x07,
0x51, 0x14, 0x66, 0xaa, 0x02, 0x53, 0x4e, 0x33, 0xa1, 0x15,
};
// TODO(crbug.com/1049317): Move the Chrome definition of these switches into
// a shared location (see
// https://source.chromium.org/chromium/chromium/src/+/master:chrome/common/chrome_switches.h;l=137;drc=66ee8f655d42c11d34d527e42f6043db540fee79).
// Contains a list of feature names for which origin trial experiments should
// be disabled. Names should be separated by "|" characters.
const char kOriginTrialDisabledFeatures[] = "origin-trial-disabled-features";
// Contains a list of token signatures for which origin trial experiments should
// be disabled. Tokens should be separated by "|" characters.
const char kOriginTrialDisabledTokens[] = "origin-trial-disabled-tokens";
// Comma-separated list of keys which will override the default public keys for
// checking origin trial tokens.
const char kOriginTrialPublicKey[] = "origin-trial-public-key";
HeadlessOriginTrialPolicy::HeadlessOriginTrialPolicy()
: public_keys_(1,
std::string(reinterpret_cast<const char*>(kDefaultPublicKey),
base::size(kDefaultPublicKey))) {
// Set the public key and disabled feature list for the origin trial key
// manager, based on the command line flags which were passed to this process.
// If the flags are not present, or are incorrectly formatted, the defaults
// will remain active.
if (base::CommandLine::InitializedForCurrentProcess()) {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(kOriginTrialPublicKey)) {
SetPublicKeysFromASCIIString(
command_line->GetSwitchValueASCII(kOriginTrialPublicKey));
}
if (command_line->HasSwitch(kOriginTrialDisabledFeatures)) {
SetDisabledFeatures(
command_line->GetSwitchValueASCII(kOriginTrialDisabledFeatures));
}
if (command_line->HasSwitch(kOriginTrialDisabledTokens)) {
SetDisabledTokens(
command_line->GetSwitchValueASCII(kOriginTrialDisabledTokens));
}
}
}
HeadlessOriginTrialPolicy::~HeadlessOriginTrialPolicy() = default;
bool HeadlessOriginTrialPolicy::IsOriginTrialsSupported() const {
return true;
}
std::vector<base::StringPiece> HeadlessOriginTrialPolicy::GetPublicKeys()
const {
std::vector<base::StringPiece> casted_public_keys;
for (auto const& key : public_keys_) {
casted_public_keys.push_back(base::StringPiece(key));
}
return casted_public_keys;
}
bool HeadlessOriginTrialPolicy::IsFeatureDisabled(
base::StringPiece feature) const {
return disabled_features_.count(feature.as_string()) > 0;
}
bool HeadlessOriginTrialPolicy::IsTokenDisabled(
base::StringPiece token_signature) const {
return disabled_tokens_.count(token_signature.as_string()) > 0;
}
bool HeadlessOriginTrialPolicy::IsOriginSecure(const GURL& url) const {
return content::IsOriginSecure(url);
}
bool HeadlessOriginTrialPolicy::SetPublicKeysFromASCIIString(
const std::string& ascii_public_keys) {
std::vector<std::string> new_public_keys;
const auto public_keys = base::SplitString(
ascii_public_keys, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
for (const auto& ascii_public_key : public_keys) {
// Base64-decode the incoming string. Set the key if it is correctly
// formatted
std::string new_public_key;
if (!base::Base64Decode(ascii_public_key, &new_public_key))
return false;
if (new_public_key.size() != 32)
return false;
new_public_keys.push_back(new_public_key);
}
if (!new_public_keys.empty()) {
public_keys_.swap(new_public_keys);
return true;
}
return false;
}
bool HeadlessOriginTrialPolicy::SetDisabledFeatures(
const std::string& disabled_feature_list) {
std::set<std::string> new_disabled_features;
const std::vector<std::string> features =
base::SplitString(disabled_feature_list, "|", base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
for (const std::string& feature : features)
new_disabled_features.insert(feature);
disabled_features_.swap(new_disabled_features);
return true;
}
bool HeadlessOriginTrialPolicy::SetDisabledTokens(
const std::string& disabled_token_list) {
std::set<std::string> new_disabled_tokens;
const std::vector<std::string> tokens =
base::SplitString(disabled_token_list, "|", base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
for (const std::string& ascii_token : tokens) {
std::string token_signature;
if (!base::Base64Decode(ascii_token, &token_signature))
continue;
if (token_signature.size() != 64)
continue;
new_disabled_tokens.insert(token_signature);
}
disabled_tokens_.swap(new_disabled_tokens);
return true;
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef HEADLESS_LIB_HEADLESS_ORIGIN_TRIAL_POLICY_H_
#define HEADLESS_LIB_HEADLESS_ORIGIN_TRIAL_POLICY_H_
#include <set>
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/strings/string_piece.h"
#include "third_party/blink/public/common/origin_trials/origin_trial_policy.h"
// This class implements an OriginTrialPolicy to allow origin trials to be
// enabled in headless mode. For more information on origin trials, see:
// 1)
// https://github.com/GoogleChrome/OriginTrials/blob/gh-pages/developer-guide.md
// 2)
// https://dev.chromium.org/blink/origin-trials/running-an-origin-trial#TOC-How-do-Origin-Trials-work-in-Chrome-
// This class is instantiated on the main/ui thread, but its methods can be
// accessed from any thread.
// TODO(crbug.com/1049317): Figure out how to share implementation with the
// other class ChromeOriginTrialPolicy.
class HeadlessOriginTrialPolicy : public blink::OriginTrialPolicy {
public:
HeadlessOriginTrialPolicy();
~HeadlessOriginTrialPolicy() override;
// blink::OriginTrialPolicy interface
bool IsOriginTrialsSupported() const override;
std::vector<base::StringPiece> GetPublicKeys() const override;
bool IsFeatureDisabled(base::StringPiece feature) const override;
bool IsTokenDisabled(base::StringPiece token_signature) const override;
bool IsOriginSecure(const GURL& url) const override;
bool SetPublicKeysFromASCIIString(const std::string& ascii_public_key);
bool SetDisabledFeatures(const std::string& disabled_feature_list);
bool SetDisabledTokens(const std::string& disabled_token_list);
private:
std::vector<std::string> public_keys_;
std::set<std::string> disabled_features_;
std::set<std::string> disabled_tokens_;
DISALLOW_COPY_AND_ASSIGN(HeadlessOriginTrialPolicy);
};
#endif // HEADLESS_LIB_HEADLESS_ORIGIN_TRIAL_POLICY_H_
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