From 4d85db38dcf2471563a07be7c1c0581968deb48a Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Wed, 1 May 2019 17:17:12 -0700 Subject: [PATCH] feat: allow setting of global fallback user agent --- atom/app/atom_content_client.cc | 32 +++++++++++++++++-- atom/app/atom_content_client.h | 6 +++- atom/browser/api/atom_api_app.cc | 16 ++++++++++ atom/browser/api/atom_api_app.h | 2 ++ atom/browser/atom_browser_context.cc | 26 +++------------ brightray/common/content_client.cc | 8 ----- brightray/common/content_client.h | 1 - docs/api/app.md | 9 ++++++ patches/common/chromium/.patches | 1 + ...expose_getcontentclient_to_embedders.patch | 24 ++++++++++++++ spec/api-app-spec.js | 24 ++++++++++++++ 11 files changed, 115 insertions(+), 34 deletions(-) create mode 100644 patches/common/chromium/chore_expose_getcontentclient_to_embedders.patch diff --git a/atom/app/atom_content_client.cc b/atom/app/atom_content_client.cc index 4107656c52afe..a596053d2f5cd 100644 --- a/atom/app/atom_content_client.cc +++ b/atom/app/atom_content_client.cc @@ -7,6 +7,7 @@ #include #include +#include "atom/browser/browser.h" #include "atom/common/atom_version.h" #include "atom/common/chrome_version.h" #include "atom/common/options_switches.h" @@ -14,6 +15,7 @@ #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/pepper_plugin_info.h" @@ -173,6 +175,14 @@ void ConvertStringWithSeparatorToVector(std::vector* 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; +} + } // namespace AtomContentClient::AtomContentClient() {} @@ -184,9 +194,25 @@ std::string AtomContentClient::GetProduct() const { } std::string AtomContentClient::GetUserAgent() const { - return content::BuildUserAgentFromProduct("Chrome/" CHROME_VERSION_STRING - " " ATOM_PRODUCT_NAME - "/" ATOM_VERSION_STRING); + 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 = "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); + } + return content::BuildUserAgentFromProduct(user_agent); + } + return user_agent_override_; +} + +void AtomContentClient::SetUserAgent(const std::string& user_agent) { + user_agent_override_ = user_agent; } base::string16 AtomContentClient::GetLocalizedString(int message_id) const { diff --git a/atom/app/atom_content_client.h b/atom/app/atom_content_client.h index b6ecc3407d0e9..e363be4236414 100644 --- a/atom/app/atom_content_client.h +++ b/atom/app/atom_content_client.h @@ -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( @@ -31,6 +33,8 @@ class AtomContentClient : public brightray::ContentClient { std::vector* cdm_host_file_paths) override; private: + std::string user_agent_override_ = ""; + DISALLOW_COPY_AND_ASSIGN(AtomContentClient); }; diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index 7d8d8b45586b6..48143ac3e70f4 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -7,6 +7,7 @@ #include #include +#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" @@ -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" @@ -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(content::GetContentClient()); + client->SetUserAgent(user_agent); +} + +std::string App::GetUserAgentFallback() { + AtomContentClient* client = + static_cast(content::GetContentClient()); + return client->GetUserAgent(); +} + #if defined(OS_MACOSX) bool App::MoveToApplicationsFolder(mate::Arguments* args) { return ui::cocoa::AtomBundleMover::Move(args); @@ -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); } diff --git a/atom/browser/api/atom_api_app.h b/atom/browser/api/atom_api_app.h index 003a0bdecfc89..18e9108bd512d 100644 --- a/atom/browser/api/atom_api_app.h +++ b/atom/browser/api/atom_api_app.h @@ -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); diff --git a/atom/browser/atom_browser_context.cc b/atom/browser/atom_browser_context.cc index dbd42a75af070..dce271147fb20 100644 --- a/atom/browser/atom_browser_context.cc +++ b/atom/browser/atom_browser_context.cc @@ -6,6 +6,7 @@ #include +#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" @@ -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" @@ -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)); @@ -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(content::GetContentClient()); + user_agent_ = client->GetUserAgent(); // Read options. base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); diff --git a/brightray/common/content_client.cc b/brightray/common/content_client.cc index 2a8331c53cec4..6b340fb5a2e61 100644 --- a/brightray/common/content_client.cc +++ b/brightray/common/content_client.cc @@ -20,10 +20,6 @@ std::string GetProductInternal() { GetApplicationVersion().c_str()); } -std::string GetBrightrayUserAgent() { - return content::BuildUserAgentFromProduct(GetProductInternal()); -} - ContentClient::ContentClient() {} ContentClient::~ContentClient() {} @@ -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); } diff --git a/brightray/common/content_client.h b/brightray/common/content_client.h index 0089ebc9d8106..0d78ad2d1696a 100644 --- a/brightray/common/content_client.h +++ b/brightray/common/content_client.h @@ -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; diff --git a/docs/api/app.md b/docs/api/app.md index 836490852a178..cfa73a9e9fa4d 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -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. diff --git a/patches/common/chromium/.patches b/patches/common/chromium/.patches index 4e8125b7c23e3..945628de73284 100644 --- a/patches/common/chromium/.patches +++ b/patches/common/chromium/.patches @@ -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 diff --git a/patches/common/chromium/chore_expose_getcontentclient_to_embedders.patch b/patches/common/chromium/chore_expose_getcontentclient_to_embedders.patch new file mode 100644 index 0000000000000..30fc7254c446b --- /dev/null +++ b/patches/common/chromium/chore_expose_getcontentclient_to_embedders.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samuel Attard +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. diff --git a/spec/api-app-spec.js b/spec/api-app-spec.js index 7d9b84ea5f93d..6be9e0bbd9d7b 100644 --- a/spec/api-app-spec.js +++ b/spec/api-app-spec.js @@ -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) + }) + }) })