15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/convert_user_script.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/base64.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h"
121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/scoped_temp_dir.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/json/json_file_value_serializer.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/path_service.h"
16868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
17868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "chrome/browser/extensions/user_script_loader.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/chrome_paths.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "crypto/sha2.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "extensions/common/constants.h"
22f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/common/extension.h"
23a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch#include "extensions/common/file_util.h"
24d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "extensions/common/manifest_constants.h"
25ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "extensions/common/user_script.h"
267dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "url/gurl.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace extensions {
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
30d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)namespace keys = manifest_keys;
31d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)namespace values = manifest_values;
32d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)scoped_refptr<Extension> ConvertUserScriptToExtension(
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& user_script_path, const GURL& original_url,
35a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const base::FilePath& extensions_dir, base::string16* error) {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string content;
3758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!base::ReadFileToString(user_script_path, &content)) {
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    *error = base::ASCIIToUTF16("Could not read source file.");
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
42010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (!base::IsStringUTF8(content)) {
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    *error = base::ASCIIToUTF16("User script must be UTF8 encoded.");
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UserScript script;
486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (!UserScriptLoader::ParseMetadataHeader(content, &script)) {
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    *error = base::ASCIIToUTF16("Invalid script header.");
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath install_temp_dir =
54a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch      file_util::GetInstallTempDir(extensions_dir);
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (install_temp_dir.empty()) {
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    *error = base::ASCIIToUTF16(
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        "Could not get path to profile temporary directory.");
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::ScopedTempDir temp_dir;
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!temp_dir.CreateUniqueTempDirUnderPath(install_temp_dir)) {
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    *error = base::ASCIIToUTF16("Could not create temporary directory.");
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create the manifest
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::DictionaryValue> root(new base::DictionaryValue);
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string script_name;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!script.name().empty() && !script.name_space().empty())
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    script_name = script.name_space() + "/" + script.name();
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    script_name = original_url.spec();
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create the public key.
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // User scripts are not signed, but the public key for an extension doubles as
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // its unique identity, and we need one of those. A user script's unique
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // identity is its namespace+name, so we hash that to create a public key.
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // There will be no corresponding private key, which means user scripts cannot
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // be auto-updated, or claimed in the gallery.
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  char raw[crypto::kSHA256Length] = {0};
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string key;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  crypto::SHA256HashString(script_name, raw, crypto::kSHA256Length);
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::Base64Encode(std::string(raw, crypto::kSHA256Length), &key);
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The script may not have a name field, but we need one for an extension. If
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // it is missing, use the filename of the original URL.
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!script.name().empty())
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    root->SetString(keys::kName, script.name());
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    root->SetString(keys::kName, original_url.ExtractFileName());
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Not all scripts have a version, but we need one. Default to 1.0 if it is
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // missing.
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!script.version().empty())
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    root->SetString(keys::kVersion, script.version());
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    root->SetString(keys::kVersion, "1.0");
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  root->SetString(keys::kDescription, script.description());
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  root->SetString(keys::kPublicKey, key);
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  root->SetBoolean(keys::kConvertedFromUserScript, true);
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
104eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::ListValue* js_files = new base::ListValue();
1053551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  js_files->Append(new base::StringValue("script.js"));
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If the script provides its own match patterns, we use those. Otherwise, we
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // generate some using the include globs.
109eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::ListValue* matches = new base::ListValue();
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!script.url_patterns().is_empty()) {
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (URLPatternSet::const_iterator i = script.url_patterns().begin();
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         i != script.url_patterns().end(); ++i) {
1133551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      matches->Append(new base::StringValue(i->GetAsString()));
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(aa): Derive tighter matches where possible.
1173551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    matches->Append(new base::StringValue("http://*/*"));
1183551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    matches->Append(new base::StringValue("https://*/*"));
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Read the exclude matches, if any are present.
122eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::ListValue* exclude_matches = new base::ListValue();
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!script.exclude_url_patterns().is_empty()) {
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (URLPatternSet::const_iterator i =
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         script.exclude_url_patterns().begin();
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         i != script.exclude_url_patterns().end(); ++i) {
1273551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      exclude_matches->Append(new base::StringValue(i->GetAsString()));
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
131eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::ListValue* includes = new base::ListValue();
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < script.globs().size(); ++i)
1333551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    includes->Append(new base::StringValue(script.globs().at(i)));
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::ListValue* excludes = new base::ListValue();
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < script.exclude_globs().size(); ++i)
1373551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    excludes->Append(new base::StringValue(script.exclude_globs().at(i)));
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::DictionaryValue* content_script = new base::DictionaryValue();
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content_script->Set(keys::kMatches, matches);
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content_script->Set(keys::kExcludeMatches, exclude_matches);
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content_script->Set(keys::kIncludeGlobs, includes);
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content_script->Set(keys::kExcludeGlobs, excludes);
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content_script->Set(keys::kJs, js_files);
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (script.run_location() == UserScript::DOCUMENT_START)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content_script->SetString(keys::kRunAt, values::kRunAtDocumentStart);
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else if (script.run_location() == UserScript::DOCUMENT_END)
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content_script->SetString(keys::kRunAt, values::kRunAtDocumentEnd);
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else if (script.run_location() == UserScript::DOCUMENT_IDLE)
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // This is the default, but store it just in case we change that.
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    content_script->SetString(keys::kRunAt, values::kRunAtDocumentIdle);
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
154eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  base::ListValue* content_scripts = new base::ListValue();
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  content_scripts->Append(content_script);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  root->Set(keys::kContentScripts, content_scripts);
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath manifest_path = temp_dir.path().Append(kManifestFilename);
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  JSONFileValueSerializer serializer(manifest_path);
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!serializer.Serialize(*root)) {
1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    *error = base::ASCIIToUTF16("Could not write JSON.");
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Write the script file.
1677dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (!base::CopyFile(user_script_path,
1687dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch                      temp_dir.path().AppendASCII("script.js"))) {
1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    *error = base::ASCIIToUTF16("Could not copy script file.");
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(rdevlin.cronin): Continue removing std::string errors and replacing
174a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  // with base::string16
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string utf8_error;
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<Extension> extension = Extension::Create(
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      temp_dir.path(),
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      Manifest::INTERNAL,
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *root,
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Extension::NO_FLAGS,
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      &utf8_error);
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  *error = base::UTF8ToUTF16(utf8_error);
183868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (!extension.get()) {
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED() << "Could not init extension " << *error;
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  temp_dir.Take();  // The caller takes ownership of the directory.
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return extension;
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace extensions
193