blob: 1003ceb4ac60e6e292278ef45883c67cbcd24528 [file] [log] [blame]
// 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