Skip to content

Commit

Permalink
feat: allow setting of global fallback user agent (#18110)
Browse files Browse the repository at this point in the history
  • Loading branch information
MarshallOfSound committed May 2, 2019
1 parent 6b16695 commit 9a665eb
Show file tree
Hide file tree
Showing 12 changed files with 143 additions and 34 deletions.
56 changes: 53 additions & 3 deletions atom/app/atom_content_client.cc
Expand Up @@ -7,15 +7,18 @@
#include <string>
#include <vector>

#include "atom/browser/browser.h"
#include "atom/common/atom_version.h"
#include "atom/common/chrome_version.h"
#include "atom/common/options_switches.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/common/content_constants.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/pepper_plugin_info.h"
#include "content/public/common/user_agent.h"
#include "electron/buildflags/buildflags.h"
Expand Down Expand Up @@ -173,6 +176,27 @@ void ConvertStringWithSeparatorToVector(std::vector<std::string>* vec,
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
}

std::string RemoveWhitespace(const std::string& str) {
std::string trimmed;
if (base::RemoveChars(str, " ", &trimmed))
return trimmed;
else
return str;
}

bool IsBrowserProcess() {
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
std::string process_type =
command_line->GetSwitchValueASCII(::switches::kProcessType);
return process_type.empty();
}

std::string BuildDefaultUserAgent() {
return "Chrome/" CHROME_VERSION_STRING " " ATOM_PRODUCT_NAME
"/" ATOM_VERSION_STRING;
}

} // namespace

AtomContentClient::AtomContentClient() {}
Expand All @@ -184,9 +208,35 @@ std::string AtomContentClient::GetProduct() const {
}

std::string AtomContentClient::GetUserAgent() const {
return content::BuildUserAgentFromProduct("Chrome/" CHROME_VERSION_STRING
" " ATOM_PRODUCT_NAME
"/" ATOM_VERSION_STRING);
if (IsBrowserProcess()) {
if (user_agent_override_.empty()) {
auto* browser = Browser::Get();
std::string name = RemoveWhitespace(browser->GetName());
std::string user_agent;
if (name == ATOM_PRODUCT_NAME) {
user_agent = BuildDefaultUserAgent();
} else {
user_agent = base::StringPrintf(
"%s/%s Chrome/%s " ATOM_PRODUCT_NAME "/" ATOM_VERSION_STRING,
name.c_str(), browser->GetVersion().c_str(), CHROME_VERSION_STRING);
}
return content::BuildUserAgentFromProduct(user_agent);
}
return user_agent_override_;
}
// In a renderer process the user agent should be provided on the CLI
// If it's not we just fallback to the default one, this should never happen
// but we want to handle it gracefully
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
std::string cli_user_agent = command_line->GetSwitchValueASCII("user-agent");
if (cli_user_agent.empty())
return BuildDefaultUserAgent();
return cli_user_agent;
}

void AtomContentClient::SetUserAgent(const std::string& user_agent) {
user_agent_override_ = user_agent;
}

base::string16 AtomContentClient::GetLocalizedString(int message_id) const {
Expand Down
6 changes: 5 additions & 1 deletion atom/app/atom_content_client.h
Expand Up @@ -18,10 +18,12 @@ class AtomContentClient : public brightray::ContentClient {
AtomContentClient();
~AtomContentClient() override;

std::string GetUserAgent() const override;
void SetUserAgent(const std::string& user_agent);

protected:
// content::ContentClient:
std::string GetProduct() const override;
std::string GetUserAgent() const override;
base::string16 GetLocalizedString(int message_id) const override;
void AddAdditionalSchemes(Schemes* schemes) override;
void AddPepperPlugins(
Expand All @@ -31,6 +33,8 @@ class AtomContentClient : public brightray::ContentClient {
std::vector<media::CdmHostFilePath>* cdm_host_file_paths) override;

private:
std::string user_agent_override_ = "";

DISALLOW_COPY_AND_ASSIGN(AtomContentClient);
};

Expand Down
16 changes: 16 additions & 0 deletions atom/browser/api/atom_api_app.cc
Expand Up @@ -7,6 +7,7 @@
#include <string>
#include <vector>

#include "atom/app/atom_content_client.h"
#include "atom/browser/api/atom_api_menu.h"
#include "atom/browser/api/atom_api_session.h"
#include "atom/browser/api/atom_api_web_contents.h"
Expand Down Expand Up @@ -43,6 +44,7 @@
#include "content/public/browser/client_certificate_delegate.h"
#include "content/public/browser/gpu_data_manager.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "media/audio/audio_manager.h"
#include "native_mate/object_template_builder.h"
Expand Down Expand Up @@ -1236,6 +1238,18 @@ void App::EnableMixedSandbox(mate::Arguments* args) {
command_line->AppendSwitch(switches::kEnableMixedSandbox);
}

void App::SetUserAgentFallback(const std::string& user_agent) {
AtomContentClient* client =
static_cast<AtomContentClient*>(content::GetContentClient());
client->SetUserAgent(user_agent);
}

std::string App::GetUserAgentFallback() {
AtomContentClient* client =
static_cast<AtomContentClient*>(content::GetContentClient());
return client->GetUserAgent();
}

#if defined(OS_MACOSX)
bool App::MoveToApplicationsFolder(mate::Arguments* args) {
return ui::cocoa::AtomBundleMover::Move(args);
Expand Down Expand Up @@ -1342,6 +1356,8 @@ void App::BuildPrototype(v8::Isolate* isolate,
.SetMethod("startAccessingSecurityScopedResource",
&App::StartAccessingSecurityScopedResource)
#endif
.SetProperty("userAgentFallback", &App::GetUserAgentFallback,
&App::SetUserAgentFallback)
.SetMethod("enableSandbox", &App::EnableSandbox)
.SetMethod("enableMixedSandbox", &App::EnableMixedSandbox);
}
Expand Down
2 changes: 2 additions & 0 deletions atom/browser/api/atom_api_app.h
Expand Up @@ -205,6 +205,8 @@ class App : public AtomBrowserClient::Delegate,
const std::string& info_type);
void EnableSandbox(mate::Arguments* args);
void EnableMixedSandbox(mate::Arguments* args);
void SetUserAgentFallback(const std::string& user_agent);
std::string GetUserAgentFallback();

#if defined(OS_MACOSX)
bool MoveToApplicationsFolder(mate::Arguments* args);
Expand Down
26 changes: 5 additions & 21 deletions atom/browser/atom_browser_context.cc
Expand Up @@ -6,6 +6,7 @@

#include <utility>

#include "atom/app/atom_content_client.h"
#include "atom/browser/atom_blob_reader.h"
#include "atom/browser/atom_browser_main_parts.h"
#include "atom/browser/atom_download_manager_delegate.h"
Expand Down Expand Up @@ -42,6 +43,7 @@
#include "components/proxy_config/proxy_config_pref_names.h"
#include "content/browser/blob_storage/chrome_blob_storage_context.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_client.h"
#include "content/public/common/user_agent.h"
#include "net/base/escape.h"

Expand All @@ -51,14 +53,6 @@ namespace atom {

namespace {

std::string RemoveWhitespace(const std::string& str) {
std::string trimmed;
if (base::RemoveChars(str, " ", &trimmed))
return trimmed;
else
return str;
}

// Convert string to lower case and escape it.
std::string MakePartitionName(const std::string& input) {
return net::EscapePath(base::ToLowerASCII(input));
Expand All @@ -78,19 +72,9 @@ AtomBrowserContext::AtomBrowserContext(const std::string& partition,
storage_policy_(new SpecialStoragePolicy),
in_memory_(in_memory),
weak_factory_(this) {
// Construct user agent string.
Browser* browser = Browser::Get();
std::string name = RemoveWhitespace(browser->GetName());
std::string user_agent;
if (name == ATOM_PRODUCT_NAME) {
user_agent = "Chrome/" CHROME_VERSION_STRING " " ATOM_PRODUCT_NAME
"/" ATOM_VERSION_STRING;
} else {
user_agent = base::StringPrintf(
"%s/%s Chrome/%s " ATOM_PRODUCT_NAME "/" ATOM_VERSION_STRING,
name.c_str(), browser->GetVersion().c_str(), CHROME_VERSION_STRING);
}
user_agent_ = content::BuildUserAgentFromProduct(user_agent);
AtomContentClient* client =
static_cast<AtomContentClient*>(content::GetContentClient());
user_agent_ = client->GetUserAgent();

// Read options.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
Expand Down
4 changes: 4 additions & 0 deletions atom/browser/web_contents_preferences.cc
Expand Up @@ -20,6 +20,7 @@
#include "cc/base/switches.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/web_preferences.h"
#include "native_mate/dictionary.h"
Expand Down Expand Up @@ -229,6 +230,9 @@ WebContentsPreferences* WebContentsPreferences::From(

void WebContentsPreferences::AppendCommandLineSwitches(
base::CommandLine* command_line) {
// Append UA Override
command_line->AppendSwitchASCII("user-agent",
content::GetContentClient()->GetUserAgent());
// Check if plugins are enabled.
if (IsEnabled(options::kPlugins))
command_line->AppendSwitch(switches::kEnablePlugins);
Expand Down
8 changes: 0 additions & 8 deletions brightray/common/content_client.cc
Expand Up @@ -20,10 +20,6 @@ std::string GetProductInternal() {
GetApplicationVersion().c_str());
}

std::string GetBrightrayUserAgent() {
return content::BuildUserAgentFromProduct(GetProductInternal());
}

ContentClient::ContentClient() {}

ContentClient::~ContentClient() {}
Expand All @@ -32,10 +28,6 @@ std::string ContentClient::GetProduct() const {
return GetProductInternal();
}

std::string ContentClient::GetUserAgent() const {
return GetBrightrayUserAgent();
}

base::string16 ContentClient::GetLocalizedString(int message_id) const {
return l10n_util::GetStringUTF16(message_id);
}
Expand Down
1 change: 0 additions & 1 deletion brightray/common/content_client.h
Expand Up @@ -21,7 +21,6 @@ class ContentClient : public content::ContentClient {

private:
std::string GetProduct() const override;
std::string GetUserAgent() const override;
base::string16 GetLocalizedString(int message_id) const override;
base::StringPiece GetDataResource(int resource_id,
ui::ScaleFactor) const override;
Expand Down
9 changes: 9 additions & 0 deletions docs/api/app.md
Expand Up @@ -1261,6 +1261,15 @@ Sets the `image` associated with this dock icon.

## Properties

### `app.userAgentFallback`

A `String` which is the user agent string Electron will use as a global fallback.

This is the user agent that will be used when no user agent is set at the
`webContents` or `session` level. Useful for ensuring your entire
app has the same user agent. Set to a custom value as early as possible
in your apps initialization to ensure that your overridden value is used.

### `app.isPackaged`

A `Boolean` property that returns `true` if the app is packaged, `false` otherwise. For many apps, this property can be used to distinguish development and production environments.
Expand Down
1 change: 1 addition & 0 deletions patches/common/chromium/.patches
Expand Up @@ -99,3 +99,4 @@ keyboard-lock-service-impl.patch
make_--explicitly-allowed-ports_work_with_networkservice.patch
fix_crashes_in_renderframeimpl_onselectpopupmenuitem_s.patch
fix_re-entracy_problem_with_invalidateframesinkid.patch
chore_expose_getcontentclient_to_embedders.patch
@@ -0,0 +1,24 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Samuel Attard <sattard@slack-corp.com>
Date: Wed, 1 May 2019 18:04:41 -0700
Subject: chore: expose GetContentClient to embedders


diff --git a/content/public/common/content_client.h b/content/public/common/content_client.h
index 528fd6abf6a623b8076803fddf5616d88f0978e8..e03bfeff5dabd55fe548ba828ae30065ba3f42d6 100644
--- a/content/public/common/content_client.h
+++ b/content/public/common/content_client.h
@@ -58,10 +58,10 @@ struct PepperPluginInfo;
// content code is called.
CONTENT_EXPORT void SetContentClient(ContentClient* client);

-#if defined(CONTENT_IMPLEMENTATION)
+//#if defined(CONTENT_IMPLEMENTATION)
// Content's embedder API should only be used by content.
-ContentClient* GetContentClient();
-#endif
+CONTENT_EXPORT ContentClient* GetContentClient();
+//#endif

// Used for tests to override the relevant embedder interfaces. Each method
// returns the old value.
24 changes: 24 additions & 0 deletions spec/api-app-spec.js
Expand Up @@ -1100,4 +1100,28 @@ describe('app module', () => {
return expect(app.whenReady()).to.be.eventually.fulfilled
})
})

describe('user agent fallback', () => {
let initialValue

before(() => {
initialValue = app.userAgentFallback
})

it('should have a reasonable default', () => {
expect(initialValue).to.include(`Electron/${process.versions.electron}`)
expect(initialValue).to.include(`Chrome/${process.versions.chrome}`)
})

it('should be overridable', () => {
app.userAgentFallback = 'test-agent/123'
expect(app.userAgentFallback).to.equal('test-agent/123')
})

it('should be restorable', () => {
app.userAgentFallback = 'test-agent/123'
app.userAgentFallback = ''
expect(app.userAgentFallback).to.equal(initialValue)
})
})
})

0 comments on commit 9a665eb

Please sign in to comment.