// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/custom_handlers/protocol_handler.h" #include "base/json/values_util.h" #include "base/strings/escape.h" #include "base/strings/utf_string_conversions.h" #include "components/strings/grit/components_strings.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/content_client.h" #include "content/public/common/origin_util.h" #include "services/network/public/cpp/is_potentially_trustworthy.h" #include "third_party/blink/public/common/custom_handlers/protocol_handler_utils.h" #include "third_party/blink/public/common/scheme_registry.h" #include "third_party/blink/public/common/security/protocol_handler_security_level.h" #include "ui/base/l10n/l10n_util.h" using content::BrowserThread; namespace custom_handlers { ProtocolHandler::ProtocolHandler( const std::string& protocol, const GURL& url, base::Time last_modified, blink::ProtocolHandlerSecurityLevel security_level) : protocol_(base::ToLowerASCII(protocol)), url_(url), last_modified_(last_modified), security_level_(security_level) {} ProtocolHandler::ProtocolHandler(const ProtocolHandler&) = default; ProtocolHandler::~ProtocolHandler() = default; ProtocolHandler ProtocolHandler::CreateProtocolHandler( const std::string& protocol, const GURL& url, blink::ProtocolHandlerSecurityLevel security_level) { DCHECK_CURRENTLY_ON(BrowserThread::UI); return ProtocolHandler(protocol, url, base::Time::Now(), security_level); } ProtocolHandler::ProtocolHandler( const std::string& protocol, const GURL& url, const std::string& app_id, base::Time last_modified, blink::ProtocolHandlerSecurityLevel security_level) : protocol_(base::ToLowerASCII(protocol)), url_(url), web_app_id_(app_id), last_modified_(last_modified), security_level_(security_level) {} // static ProtocolHandler ProtocolHandler::CreateWebAppProtocolHandler( const std::string& protocol, const GURL& url, const std::string& app_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); return ProtocolHandler(protocol, url, app_id, base::Time::Now(), blink::ProtocolHandlerSecurityLevel::kStrict); } ProtocolHandler::ProtocolHandler() = default; bool ProtocolHandler::IsValidDict(const base::Value::Dict& value) { // Note that "title" parameter is ignored. // The |last_modified| field is optional as it was introduced in M68. return value.FindString("protocol") && value.FindString("url"); } bool ProtocolHandler::IsValid() const { DCHECK_CURRENTLY_ON(BrowserThread::UI); // We don't want to include URL's syntax checks because there are use cases of // the protocol handlers logic that require more flexibility than the one // specified for the registerProtocolHandler API (eg, Predefined Handlers). if (!blink::IsAllowedCustomHandlerURL(url_, security_level_)) return false; return blink::IsValidCustomHandlerScheme(protocol_, security_level_); } bool ProtocolHandler::IsSameOrigin(const ProtocolHandler& handler) const { DCHECK_CURRENTLY_ON(BrowserThread::UI); return handler.url().DeprecatedGetOriginAsURL() == url_.DeprecatedGetOriginAsURL(); } const ProtocolHandler& ProtocolHandler::EmptyProtocolHandler() { DCHECK_CURRENTLY_ON(BrowserThread::UI); static const ProtocolHandler* const kEmpty = new ProtocolHandler(); return *kEmpty; } ProtocolHandler ProtocolHandler::CreateProtocolHandler( const base::Value::Dict& value) { DCHECK_CURRENTLY_ON(BrowserThread::UI); if (!IsValidDict(value)) { return EmptyProtocolHandler(); } std::string protocol, url; // |time| defaults to the beginning of time if it is not specified. base::Time time; blink::ProtocolHandlerSecurityLevel security_level = blink::ProtocolHandlerSecurityLevel::kStrict; if (const std::string* protocol_in = value.FindString("protocol")) protocol = *protocol_in; if (const std::string* url_in = value.FindString("url")) url = *url_in; absl::optional time_value = base::ValueToTime(value.Find("last_modified")); // Treat invalid times as the default value. if (time_value) time = *time_value; absl::optional security_level_value = value.FindInt("security_level"); if (security_level_value) { security_level = blink::ProtocolHandlerSecurityLevelFrom(*security_level_value); } if (const base::Value* app_id_val = value.Find("app_id")) { std::string app_id; if (app_id_val->is_string()) app_id = app_id_val->GetString(); return ProtocolHandler(protocol, GURL(url), app_id, time, security_level); } return ProtocolHandler(protocol, GURL(url), time, security_level); } GURL ProtocolHandler::TranslateUrl(const GURL& url) const { DCHECK_CURRENTLY_ON(BrowserThread::UI); std::string translatedUrlSpec(url_.spec()); base::ReplaceFirstSubstringAfterOffset( &translatedUrlSpec, 0, "%s", base::EscapeQueryParamValue(url.spec(), false)); return GURL(translatedUrlSpec); } base::Value::Dict ProtocolHandler::Encode() const { DCHECK_CURRENTLY_ON(BrowserThread::UI); base::Value::Dict d; d.Set("protocol", protocol_); d.Set("url", url_.spec()); d.Set("last_modified", base::TimeToValue(last_modified_)); d.Set("security_level", static_cast(security_level_)); if (web_app_id_.has_value()) d.Set("app_id", web_app_id_.value()); return d; } std::u16string ProtocolHandler::GetProtocolDisplayName( const std::string& protocol) { DCHECK_CURRENTLY_ON(BrowserThread::UI); if (protocol == "mailto") return l10n_util::GetStringUTF16(IDS_REGISTER_PROTOCOL_HANDLER_MAILTO_NAME); if (protocol == "webcal") return l10n_util::GetStringUTF16(IDS_REGISTER_PROTOCOL_HANDLER_WEBCAL_NAME); return base::UTF8ToUTF16(protocol); } std::u16string ProtocolHandler::GetProtocolDisplayName() const { DCHECK_CURRENTLY_ON(BrowserThread::UI); return GetProtocolDisplayName(protocol_); } #if !defined(NDEBUG) std::string ProtocolHandler::ToString() const { return "{ protocol=" + protocol_ + ", url=" + url_.spec() + " }"; } #endif bool ProtocolHandler::operator==(const ProtocolHandler& other) const { return protocol_ == other.protocol_ && url_ == other.url_; } bool ProtocolHandler::IsEquivalent(const ProtocolHandler& other) const { return protocol_ == other.protocol_ && url_ == other.url_; } bool ProtocolHandler::operator<(const ProtocolHandler& other) const { return url_ < other.url_; } } // namespace custom_handlers