172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/convert_user_script.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <string> 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <vector> 9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/base64.h" 11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/file_path.h" 12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/file_util.h" 13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/scoped_temp_dir.h" 14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/path_service.h" 15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h" 16ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "crypto/sha2.h" 17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/user_script_master.h" 18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_paths.h" 19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension.h" 20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension_constants.h" 2172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/common/extensions/extension_file_util.h" 22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/user_script.h" 23ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/json_value_serializer.h" 24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "googleurl/src/gurl.h" 25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace keys = extension_manifest_keys; 27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 28513209b27ff55e2841eac0e4120199c23acce758Ben Murdochscoped_refptr<Extension> ConvertUserScriptToExtension( 29513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch const FilePath& user_script_path, const GURL& original_url, 30513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch std::string* error) { 31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string content; 32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!file_util::ReadFileToString(user_script_path, &content)) { 3372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen *error = "Could not read source file."; 34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return NULL; 35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 3721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen if (!IsStringUTF8(content)) { 3821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen *error = "User script must be UTF8 encoded."; 3921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen return NULL; 4021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen } 4121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen 42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch UserScript script; 43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!UserScriptMaster::ScriptReloader::ParseMetadataHeader(content, 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &script)) { 45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *error = "Invalid script header."; 46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return NULL; 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 4972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen FilePath user_data_temp_dir = extension_file_util::GetUserDataTempDir(); 5072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (user_data_temp_dir.empty()) { 5172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen *error = "Could not get path to profile temporary directory."; 5272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return NULL; 5372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ScopedTempDir temp_dir; 563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!temp_dir.CreateUniqueTempDirUnderPath(user_data_temp_dir)) { 57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *error = "Could not create temporary directory."; 58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return NULL; 59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Create the manifest 62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch scoped_ptr<DictionaryValue> root(new DictionaryValue); 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string script_name; 64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!script.name().empty() && !script.name_space().empty()) 65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch script_name = script.name_space() + "/" + script.name(); 66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch else 67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch script_name = original_url.spec(); 68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Create the public key. 70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // User scripts are not signed, but the public key for an extension doubles as 71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // its unique identity, and we need one of those. A user script's unique 72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // identity is its namespace+name, so we hash that to create a public key. 73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // There will be no corresponding private key, which means user scripts cannot 74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // be auto-updated, or claimed in the gallery. 75ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen char raw[crypto::SHA256_LENGTH] = {0}; 76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string key; 77ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen crypto::SHA256HashString(script_name, raw, crypto::SHA256_LENGTH); 78ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen base::Base64Encode(std::string(raw, crypto::SHA256_LENGTH), &key); 79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The script may not have a name field, but we need one for an extension. If 81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // it is missing, use the filename of the original URL. 82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!script.name().empty()) 83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch root->SetString(keys::kName, script.name()); 84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch else 85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch root->SetString(keys::kName, original_url.ExtractFileName()); 86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Not all scripts have a version, but we need one. Default to 1.0 if it is 88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // missing. 89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!script.version().empty()) 90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch root->SetString(keys::kVersion, script.version()); 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch else 92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch root->SetString(keys::kVersion, "1.0"); 93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch root->SetString(keys::kDescription, script.description()); 95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch root->SetString(keys::kPublicKey, key); 96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch root->SetBoolean(keys::kConvertedFromUserScript, true); 97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ListValue* js_files = new ListValue(); 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch js_files->Append(Value::CreateStringValue("script.js")); 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If the script provides its own match patterns, we use those. Otherwise, we 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // generate some using the include globs. 103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ListValue* matches = new ListValue(); 104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!script.url_patterns().empty()) { 105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (size_t i = 0; i < script.url_patterns().size(); ++i) { 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches->Append(Value::CreateStringValue( 107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch script.url_patterns()[i].GetAsString())); 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(aa): Derive tighter matches where possible. 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches->Append(Value::CreateStringValue("http://*/*")); 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch matches->Append(Value::CreateStringValue("https://*/*")); 113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ListValue* includes = new ListValue(); 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (size_t i = 0; i < script.globs().size(); ++i) 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch includes->Append(Value::CreateStringValue(script.globs().at(i))); 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ListValue* excludes = new ListValue(); 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (size_t i = 0; i < script.exclude_globs().size(); ++i) 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch excludes->Append(Value::CreateStringValue(script.exclude_globs().at(i))); 122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DictionaryValue* content_script = new DictionaryValue(); 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch content_script->Set(keys::kMatches, matches); 125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch content_script->Set(keys::kIncludeGlobs, includes); 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch content_script->Set(keys::kExcludeGlobs, excludes); 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch content_script->Set(keys::kJs, js_files); 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ListValue* content_scripts = new ListValue(); 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch content_scripts->Append(content_script); 131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch root->Set(keys::kContentScripts, content_scripts); 133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath manifest_path = temp_dir.path().Append( 135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Extension::kManifestFilename); 136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch JSONFileValueSerializer serializer(manifest_path); 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!serializer.Serialize(*root)) { 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *error = "Could not write JSON."; 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return NULL; 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Write the script file. 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!file_util::CopyFile(user_script_path, 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch temp_dir.path().AppendASCII("script.js"))) { 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *error = "Could not copy script file."; 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return NULL; 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 149513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch scoped_refptr<Extension> extension = Extension::Create( 150dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen temp_dir.path(), 151dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen Extension::INTERNAL, 152dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen *root, 153ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen Extension::NO_FLAGS, 154dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen error); 155513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (!extension) { 156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NOTREACHED() << "Could not init extension " << *error; 157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return NULL; 158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch temp_dir.Take(); // The caller takes ownership of the directory. 161513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch return extension; 162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 163