blob: 4b3efcea778508bf868acd04ce3114dce7c7b61a [file] [log] [blame]
[email protected]ffbec692012-02-26 20:26:421// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]6657afa62009-11-04 02:15:202// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/convert_user_script.h"
6
avia2f4804a2015-12-24 23:11:137#include <stddef.h>
8
dcheng98e96a72016-06-11 03:41:489#include <memory>
[email protected]6657afa62009-11-04 02:15:2010#include <string>
dcheng98e96a72016-06-11 03:41:4811#include <utility>
[email protected]6657afa62009-11-04 02:15:2012#include <vector>
13
[email protected]978df342009-11-24 06:21:5314#include "base/base64.h"
[email protected]57999812013-02-24 05:40:5215#include "base/files/file_path.h"
thestig18dfb7a52014-08-26 10:44:0416#include "base/files/file_util.h"
[email protected]ea1a3f62012-11-16 20:34:2317#include "base/files/scoped_temp_dir.h"
[email protected]ffbec692012-02-26 20:26:4218#include "base/json/json_file_value_serializer.h"
[email protected]00e7bef2013-06-10 20:35:1719#include "base/strings/string_util.h"
[email protected]112158af2013-06-07 23:46:1820#include "base/strings/utf_string_conversions.h"
vabr9984ea62017-04-10 11:33:4921#include "base/values.h"
[email protected]69f27a22010-07-16 13:49:3122#include "chrome/common/chrome_paths.h"
[email protected]e0785902011-05-19 23:34:1723#include "crypto/sha2.h"
hanxi3b2b3df2015-02-24 15:28:0724#include "extensions/browser/extension_user_script_loader.h"
Karandeep Bhatiaaac584a22020-11-05 12:32:2725#include "extensions/common/api/content_scripts.h"
[email protected]993da5e2013-03-23 21:25:1626#include "extensions/common/constants.h"
[email protected]e4452d32013-11-15 23:07:4127#include "extensions/common/extension.h"
[email protected]85df9d12014-04-15 17:02:1428#include "extensions/common/file_util.h"
[email protected]0c3c9732013-09-16 08:53:4129#include "extensions/common/manifest_constants.h"
Julie Jeongeun Kim378db14d2021-03-05 01:53:0030#include "extensions/common/mojom/run_location.mojom-shared.h"
[email protected]49d9b142013-07-19 08:50:2731#include "extensions/common/user_script.h"
[email protected]a6483d22013-07-03 22:11:0032#include "url/gurl.h"
[email protected]6657afa62009-11-04 02:15:2033
[email protected]20f97c92012-07-13 23:12:3734namespace extensions {
35
[email protected]66e4eb32010-10-27 20:37:4136scoped_refptr<Extension> ConvertUserScriptToExtension(
Jan Wilken Dörrief27844b2021-03-11 23:18:4837 const base::FilePath& user_script_path,
38 const GURL& original_url,
39 const base::FilePath& extensions_dir,
40 std::u16string* error) {
Karandeep Bhatiaaac584a22020-11-05 12:32:2741 using ContentScript = api::content_scripts::ContentScript;
42
[email protected]6657afa62009-11-04 02:15:2043 std::string content;
[email protected]82f84b92013-08-30 18:23:5044 if (!base::ReadFileToString(user_script_path, &content)) {
Jan Wilken Dörrie78e88d82e2021-03-23 15:24:2245 *error = u"Could not read source file.";
kylecharb59c3d72019-10-29 05:26:2646 return nullptr;
[email protected]6657afa62009-11-04 02:15:2047 }
48
[email protected]527965412014-05-07 14:38:2649 if (!base::IsStringUTF8(content)) {
Jan Wilken Dörrie78e88d82e2021-03-23 15:24:2250 *error = u"User script must be UTF8 encoded.";
kylecharb59c3d72019-10-29 05:26:2651 return nullptr;
[email protected]a4d5e0072010-12-13 18:41:2552 }
53
[email protected]6657afa62009-11-04 02:15:2054 UserScript script;
[email protected]15ad2ee2014-08-15 19:15:2655 if (!UserScriptLoader::ParseMetadataHeader(content, &script)) {
Jan Wilken Dörrie78e88d82e2021-03-23 15:24:2256 *error = u"Invalid script header.";
kylecharb59c3d72019-10-29 05:26:2657 return nullptr;
[email protected]6657afa62009-11-04 02:15:2058 }
59
[email protected]650b2d52013-02-10 03:41:4560 base::FilePath install_temp_dir =
[email protected]85df9d12014-04-15 17:02:1461 file_util::GetInstallTempDir(extensions_dir);
[email protected]171ab92d2012-10-19 01:16:3462 if (install_temp_dir.empty()) {
Jan Wilken Dörrie522370fb2021-04-16 17:22:3963 *error = u"Could not get path to profile temporary directory.";
kylecharb59c3d72019-10-29 05:26:2664 return nullptr;
[email protected]650852e2011-01-19 13:26:0265 }
[email protected]69f27a22010-07-16 13:49:3166
[email protected]ea1a3f62012-11-16 20:34:2367 base::ScopedTempDir temp_dir;
[email protected]171ab92d2012-10-19 01:16:3468 if (!temp_dir.CreateUniqueTempDirUnderPath(install_temp_dir)) {
Jan Wilken Dörrie78e88d82e2021-03-23 15:24:2269 *error = u"Could not create temporary directory.";
kylecharb59c3d72019-10-29 05:26:2670 return nullptr;
[email protected]6657afa62009-11-04 02:15:2071 }
72
73 // Create the manifest
dchengc963c7142016-04-08 03:55:2274 std::unique_ptr<base::DictionaryValue> root(new base::DictionaryValue);
[email protected]6657afa62009-11-04 02:15:2075 std::string script_name;
76 if (!script.name().empty() && !script.name_space().empty())
77 script_name = script.name_space() + "/" + script.name();
78 else
79 script_name = original_url.spec();
80
81 // Create the public key.
82 // User scripts are not signed, but the public key for an extension doubles as
83 // its unique identity, and we need one of those. A user script's unique
84 // identity is its namespace+name, so we hash that to create a public key.
85 // There will be no corresponding private key, which means user scripts cannot
86 // be auto-updated, or claimed in the gallery.
[email protected]ea73fc72011-09-22 21:24:5087 char raw[crypto::kSHA256Length] = {0};
[email protected]6657afa62009-11-04 02:15:2088 std::string key;
[email protected]ea73fc72011-09-22 21:24:5089 crypto::SHA256HashString(script_name, raw, crypto::kSHA256Length);
iceman35ea7532016-04-07 19:36:3390 base::Base64Encode(base::StringPiece(raw, crypto::kSHA256Length), &key);
[email protected]6657afa62009-11-04 02:15:2091
92 // The script may not have a name field, but we need one for an extension. If
93 // it is missing, use the filename of the original URL.
94 if (!script.name().empty())
oscarjohansson77164012018-06-11 07:00:1195 root->SetString(manifest_keys::kName, script.name());
[email protected]6657afa62009-11-04 02:15:2096 else
oscarjohansson77164012018-06-11 07:00:1197 root->SetString(manifest_keys::kName, original_url.ExtractFileName());
[email protected]6657afa62009-11-04 02:15:2098
[email protected]abae38bb2010-06-22 05:20:2599 // Not all scripts have a version, but we need one. Default to 1.0 if it is
100 // missing.
101 if (!script.version().empty())
oscarjohansson77164012018-06-11 07:00:11102 root->SetString(manifest_keys::kVersion, script.version());
[email protected]abae38bb2010-06-22 05:20:25103 else
oscarjohansson77164012018-06-11 07:00:11104 root->SetString(manifest_keys::kVersion, "1.0");
[email protected]abae38bb2010-06-22 05:20:25105
oscarjohansson77164012018-06-11 07:00:11106 root->SetString(manifest_keys::kDescription, script.description());
107 root->SetString(manifest_keys::kPublicKey, key);
108 root->SetBoolean(manifest_keys::kConvertedFromUserScript, true);
[email protected]6657afa62009-11-04 02:15:20109
[email protected]6657afa62009-11-04 02:15:20110 // If the script provides its own match patterns, we use those. Otherwise, we
111 // generate some using the include globs.
Karandeep Bhatiaaac584a22020-11-05 12:32:27112 std::vector<std::string> matches;
[email protected]06e8b8ff2011-07-13 15:03:47113 if (!script.url_patterns().is_empty()) {
Karandeep Bhatiaaac584a22020-11-05 12:32:27114 matches.reserve(script.url_patterns().size());
115 for (const URLPattern& pattern : script.url_patterns())
116 matches.push_back(pattern.GetAsString());
[email protected]6657afa62009-11-04 02:15:20117 } else {
118 // TODO(aa): Derive tighter matches where possible.
Karandeep Bhatiaaac584a22020-11-05 12:32:27119 matches.push_back("http://*/*");
120 matches.push_back("https://*/*");
[email protected]6657afa62009-11-04 02:15:20121 }
122
[email protected]2c17977c2011-12-19 07:05:43123 // Read the exclude matches, if any are present.
Karandeep Bhatiaaac584a22020-11-05 12:32:27124 std::vector<std::string> exclude_matches;
125 exclude_matches.reserve(script.exclude_url_patterns().size());
126 for (const URLPattern& pattern : script.exclude_url_patterns())
127 exclude_matches.push_back(pattern.GetAsString());
128
129 ContentScript content_script;
130 content_script.matches = std::move(matches);
131 content_script.exclude_matches =
132 std::make_unique<std::vector<std::string>>(std::move(exclude_matches));
133 content_script.include_globs =
134 std::make_unique<std::vector<std::string>>(script.globs());
135 content_script.exclude_globs =
136 std::make_unique<std::vector<std::string>>(script.exclude_globs());
137
138 content_script.js = std::make_unique<std::vector<std::string>>();
139 content_script.js->push_back("script.js");
140
Julie Jeongeun Kim378db14d2021-03-05 01:53:00141 if (script.run_location() == mojom::RunLocation::kDocumentStart) {
Karandeep Bhatiaaac584a22020-11-05 12:32:27142 content_script.run_at = api::content_scripts::RUN_AT_DOCUMENT_START;
Julie Jeongeun Kim378db14d2021-03-05 01:53:00143 } else if (script.run_location() == mojom::RunLocation::kDocumentEnd) {
Karandeep Bhatiaaac584a22020-11-05 12:32:27144 content_script.run_at = api::content_scripts::RUN_AT_DOCUMENT_END;
Julie Jeongeun Kim378db14d2021-03-05 01:53:00145 } else if (script.run_location() == mojom::RunLocation::kDocumentIdle) {
Karandeep Bhatiaaac584a22020-11-05 12:32:27146 // This is the default, but store it just in case we change that.
147 content_script.run_at = api::content_scripts::RUN_AT_DOCUMENT_IDLE;
[email protected]2c17977c2011-12-19 07:05:43148 }
149
Song Fangzhen06c83d52021-06-24 03:51:27150 base::Value content_scripts(base::Value::Type::LIST);
151 content_scripts.Append(
152 base::Value::FromUniquePtrValue(content_script.ToValue()));
153 root->SetKey(api::content_scripts::ManifestKeys::kContentScripts,
154 std::move(content_scripts));
[email protected]6657afa62009-11-04 02:15:20155
vabr9142fe22016-09-08 13:19:22156 base::FilePath manifest_path = temp_dir.GetPath().Append(kManifestFilename);
[email protected]6657afa62009-11-04 02:15:20157 JSONFileValueSerializer serializer(manifest_path);
158 if (!serializer.Serialize(*root)) {
Jan Wilken Dörrie78e88d82e2021-03-23 15:24:22159 *error = u"Could not write JSON.";
kylecharb59c3d72019-10-29 05:26:26160 return nullptr;
[email protected]6657afa62009-11-04 02:15:20161 }
162
163 // Write the script file.
[email protected]f0ff2ad2013-07-09 17:42:26164 if (!base::CopyFile(user_script_path,
vabr9142fe22016-09-08 13:19:22165 temp_dir.GetPath().AppendASCII("script.js"))) {
Jan Wilken Dörrie78e88d82e2021-03-23 15:24:22166 *error = u"Could not copy script file.";
kylecharb59c3d72019-10-29 05:26:26167 return nullptr;
[email protected]6657afa62009-11-04 02:15:20168 }
169
[email protected]fc670822011-12-17 09:33:49170 // TODO(rdevlin.cronin): Continue removing std::string errors and replacing
Jan Wilken Dörrief27844b2021-03-11 23:18:48171 // with std::u16string
[email protected]fc670822011-12-17 09:33:49172 std::string utf8_error;
vabr9142fe22016-09-08 13:19:22173 scoped_refptr<Extension> extension =
Gyuyoung Kimabc23382021-03-18 03:09:18174 Extension::Create(temp_dir.GetPath(), mojom::ManifestLocation::kInternal,
175 *root, Extension::NO_FLAGS, &utf8_error);
[email protected]04338722013-12-24 23:18:05176 *error = base::UTF8ToUTF16(utf8_error);
[email protected]dc24976f2013-06-02 21:15:09177 if (!extension.get()) {
[email protected]6657afa62009-11-04 02:15:20178 NOTREACHED() << "Could not init extension " << *error;
kylecharb59c3d72019-10-29 05:26:26179 return nullptr;
[email protected]6657afa62009-11-04 02:15:20180 }
181
182 temp_dir.Take(); // The caller takes ownership of the directory.
[email protected]66e4eb32010-10-27 20:37:41183 return extension;
[email protected]6657afa62009-11-04 02:15:20184}
[email protected]20f97c92012-07-13 23:12:37185
186} // namespace extensions