| // Copyright 2019 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 "content/browser/web_package/web_bundle_utils.h" |
| |
| #include "base/command_line.h" |
| #include "base/feature_list.h" |
| #include "build/build_config.h" |
| #include "content/browser/web_package/web_bundle_url_loader_factory.h" |
| #include "content/public/browser/content_browser_client.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/content_switches.h" |
| #include "url/gurl.h" |
| |
| #if defined(OS_ANDROID) |
| #include "url/url_constants.h" |
| #endif // defined(OS_ANDROID) |
| |
| namespace content { |
| namespace web_bundle_utils { |
| namespace { |
| |
| const base::FilePath::CharType kWebBundleFileExtension[] = |
| FILE_PATH_LITERAL(".wbn"); |
| |
| } // namespace |
| |
| const net::NetworkTrafficAnnotationTag kTrafficAnnotation = |
| net::DefineNetworkTrafficAnnotation("web_bundle_loader", |
| R"( |
| semantics { |
| sender: "Web Bundle Loader" |
| description: |
| "Navigation request and subresource request inside a Web Bundle. " |
| "This does not trigger any network transaction directly, but " |
| "access to an entry in a local file, or in a previously fetched " |
| "resource over network." |
| trigger: "The user navigates to a Web Bundle." |
| data: "Nothing." |
| destination: LOCAL |
| } |
| policy { |
| cookies_allowed: NO |
| setting: "These requests cannot be disabled in settings." |
| policy_exception_justification: |
| "Not implemented. This request does not make any network transaction." |
| } |
| comments: |
| "Usually the request accesses an entry in a local file or a previously " |
| "fetched resource over network that contains multiple archived entries. " |
| "But once the feature is exposed to the public web API, the archive file " |
| "can be streamed over network. In such case, the streaming should be " |
| "provided by another URLLoader request that is issued by Blink, but based " |
| "on a user initiated navigation." |
| )"); |
| |
| bool IsSupportedFileScheme(const GURL& url) { |
| if (url.SchemeIsFile()) |
| return true; |
| #if defined(OS_ANDROID) |
| if (url.SchemeIs(url::kContentScheme)) |
| return true; |
| #endif // defined(OS_ANDROID) |
| return false; |
| } |
| |
| bool CanLoadAsTrustableWebBundleFile(const GURL& url) { |
| if (!GetContentClient()->browser()->CanAcceptUntrustedExchangesIfNeeded()) |
| return false; |
| if (!IsSupportedFileScheme(url)) |
| return false; |
| if (!base::CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kTrustableWebBundleFileUrl)) { |
| return false; |
| } |
| return base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
| switches::kTrustableWebBundleFileUrl) == url.spec(); |
| } |
| |
| bool CanLoadAsWebBundleFile(const GURL& url) { |
| if (!base::FeatureList::IsEnabled(features::kWebBundles)) |
| return false; |
| return IsSupportedFileScheme(url); |
| } |
| |
| bool CanLoadAsWebBundle(const GURL& url, const std::string& mime_type) { |
| if (!base::FeatureList::IsEnabled(features::kWebBundles)) |
| return false; |
| // Currently loading Web Bundle file from server response is not |
| // implemented yet. |
| if (!IsSupportedFileScheme(url)) |
| return false; |
| return mime_type == kWebBundleFileMimeTypeWithoutParameters; |
| } |
| |
| bool GetWebBundleFileMimeTypeFromFile(const base::FilePath& path, |
| std::string* mime_type) { |
| DCHECK(mime_type); |
| if (!base::FeatureList::IsEnabled(features::kWebBundles)) |
| return false; |
| if (path.Extension() != kWebBundleFileExtension) |
| return false; |
| *mime_type = kWebBundleFileMimeTypeWithoutParameters; |
| return true; |
| } |
| |
| GURL GetSynthesizedUrlForWebBundle(const GURL& web_bundle_file_url, |
| const GURL& url_in_bundles) { |
| url::Replacements<char> replacements; |
| |
| url::Replacements<char> clear_ref; |
| clear_ref.ClearRef(); |
| std::string query_string = url_in_bundles.ReplaceComponents(clear_ref).spec(); |
| url::Component new_query(0, query_string.size()); |
| replacements.SetQuery(query_string.c_str(), new_query); |
| |
| if (!url_in_bundles.has_ref()) { |
| replacements.ClearRef(); |
| return web_bundle_file_url.ReplaceComponents(replacements); |
| } |
| url::Component new_ref(0, url_in_bundles.ref().size()); |
| std::string ref_string = url_in_bundles.ref(); |
| replacements.SetRef(ref_string.c_str(), new_ref); |
| return web_bundle_file_url.ReplaceComponents(replacements); |
| } |
| |
| void CompleteWithInvalidWebBundleError( |
| mojo::Remote<network::mojom::URLLoaderClient> client, |
| int frame_tree_node_id, |
| const std::string& error_message) { |
| WebContents* web_contents = |
| WebContents::FromFrameTreeNodeId(frame_tree_node_id); |
| if (web_contents) { |
| web_contents->GetMainFrame()->AddMessageToConsole( |
| blink::mojom::ConsoleMessageLevel::kError, error_message); |
| } |
| std::move(client)->OnComplete( |
| network::URLLoaderCompletionStatus(net::ERR_INVALID_WEB_BUNDLE)); |
| } |
| |
| std::string GetMetadataParseErrorMessage( |
| const web_package::mojom::BundleMetadataParseErrorPtr& metadata_error) { |
| return std::string("Failed to read metadata of Web Bundle file: ") + |
| metadata_error->message; |
| } |
| |
| } // namespace web_bundle_utils |
| } // namespace content |