summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions/convert_user_script.cc
blob: 404f2af812d613bcdf37c135d44cf10c5aa94368 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// Copyright (c) 2009 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 "chrome/browser/extensions/convert_user_script.h"

#include <string>
#include <vector>

#include "base/base64.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/scoped_temp_dir.h"
#include "base/sha2.h"
#include "base/string_util.h"
#include "chrome/browser/extensions/user_script_master.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/extensions/user_script.h"
#include "chrome/common/json_value_serializer.h"
#include "googleurl/src/gurl.h"

namespace keys = extension_manifest_keys;

Extension* ConvertUserScriptToExtension(const FilePath& user_script_path,
                                        const GURL& original_url,
                                        std::string* error) {
  std::string content;
  if (!file_util::ReadFileToString(user_script_path, &content)) {
    *error = "Could not read source file: " +
        WideToASCII(user_script_path.ToWStringHack());
    return NULL;
  }

  UserScript script;
  if (!UserScriptMaster::ScriptReloader::ParseMetadataHeader(content,
                                                             &script)) {
    *error = "Invalid script header.";
    return NULL;
  }

  ScopedTempDir temp_dir;
  if (!temp_dir.CreateUniqueTempDir()) {
    *error = "Could not create temporary directory.";
    return NULL;
  }

  // Create the manifest
  scoped_ptr<DictionaryValue> root(new DictionaryValue);
  std::string script_name;
  if (!script.name().empty() && !script.name_space().empty())
    script_name = script.name_space() + "/" + script.name();
  else
    script_name = original_url.spec();

  // Create the public key.
  // User scripts are not signed, but the public key for an extension doubles as
  // its unique identity, and we need one of those. A user script's unique
  // identity is its namespace+name, so we hash that to create a public key.
  // There will be no corresponding private key, which means user scripts cannot
  // be auto-updated, or claimed in the gallery.
  char raw[base::SHA256_LENGTH] = {0};
  std::string key;
  base::SHA256HashString(script_name, raw, base::SHA256_LENGTH);
  base::Base64Encode(std::string(raw, base::SHA256_LENGTH), &key);

  // The script may not have a name field, but we need one for an extension. If
  // it is missing, use the filename of the original URL.
  if (!script.name().empty())
    root->SetString(keys::kName, script.name());
  else
    root->SetString(keys::kName, original_url.ExtractFileName());

  root->SetString(keys::kDescription, script.description());
  root->SetString(keys::kVersion, "1.0");
  root->SetString(keys::kPublicKey, key);
  root->SetBoolean(keys::kConvertedFromUserScript, true);

  ListValue* js_files = new ListValue();
  js_files->Append(Value::CreateStringValue("script.js"));

  // If the script provides its own match patterns, we use those. Otherwise, we
  // generate some using the include globs.
  ListValue* matches = new ListValue();
  if (!script.url_patterns().empty()) {
    for (size_t i = 0; i < script.url_patterns().size(); ++i) {
      matches->Append(Value::CreateStringValue(
          script.url_patterns()[i].GetAsString()));
    }
  } else {
    // TODO(aa): Derive tighter matches where possible.
    matches->Append(Value::CreateStringValue("http://*/*"));
    matches->Append(Value::CreateStringValue("https://*/*"));
  }

  ListValue* includes = new ListValue();
  for (size_t i = 0; i < script.globs().size(); ++i)
    includes->Append(Value::CreateStringValue(script.globs().at(i)));

  ListValue* excludes = new ListValue();
  for (size_t i = 0; i < script.exclude_globs().size(); ++i)
    excludes->Append(Value::CreateStringValue(script.exclude_globs().at(i)));

  DictionaryValue* content_script = new DictionaryValue();
  content_script->Set(keys::kMatches, matches);
  content_script->Set(keys::kIncludeGlobs, includes);
  content_script->Set(keys::kExcludeGlobs, excludes);
  content_script->Set(keys::kJs, js_files);

  ListValue* content_scripts = new ListValue();
  content_scripts->Append(content_script);

  root->Set(keys::kContentScripts, content_scripts);

  FilePath manifest_path = temp_dir.path().Append(
      Extension::kManifestFilename);
  JSONFileValueSerializer serializer(manifest_path);
  if (!serializer.Serialize(*root)) {
    *error = "Could not write JSON.";
    return NULL;
  }

  // Write the script file.
  if (!file_util::CopyFile(user_script_path,
                           temp_dir.path().AppendASCII("script.js"))) {
    *error = "Could not copy script file.";
    return NULL;
  }

  scoped_ptr<Extension> extension(new Extension(temp_dir.path()));
  if (!extension->InitFromValue(*root, false, error)) {
    NOTREACHED() << "Could not init extension " << *error;
    return NULL;
  }

  temp_dir.Take();  // The caller takes ownership of the directory.
  return extension.release();
}