1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "extensions/browser/info_map.h"
6
7#include "base/strings/string_util.h"
8#include "content/public/browser/browser_thread.h"
9#include "extensions/browser/content_verifier.h"
10#include "extensions/common/constants.h"
11#include "extensions/common/extension.h"
12#include "extensions/common/extension_resource.h"
13#include "extensions/common/extension_set.h"
14#include "extensions/common/manifest_handlers/incognito_info.h"
15#include "extensions/common/manifest_handlers/shared_module_info.h"
16#include "extensions/common/permissions/permissions_data.h"
17#include "url/gurl.h"
18
19using content::BrowserThread;
20
21namespace extensions {
22
23namespace {
24
25void CheckOnValidThread() { DCHECK_CURRENTLY_ON(BrowserThread::IO); }
26
27}  // namespace
28
29struct InfoMap::ExtraData {
30  // When the extension was installed.
31  base::Time install_time;
32
33  // True if the user has allowed this extension to run in incognito mode.
34  bool incognito_enabled;
35
36  // True if the user has disabled notifications for this extension manually.
37  bool notifications_disabled;
38
39  ExtraData();
40  ~ExtraData();
41};
42
43InfoMap::ExtraData::ExtraData()
44    : incognito_enabled(false), notifications_disabled(false) {
45}
46
47InfoMap::ExtraData::~ExtraData() {}
48
49InfoMap::InfoMap() : signin_process_id_(-1) {
50}
51
52void InfoMap::AddExtension(const Extension* extension,
53                           base::Time install_time,
54                           bool incognito_enabled,
55                           bool notifications_disabled) {
56  CheckOnValidThread();
57  extensions_.Insert(extension);
58  disabled_extensions_.Remove(extension->id());
59
60  extra_data_[extension->id()].install_time = install_time;
61  extra_data_[extension->id()].incognito_enabled = incognito_enabled;
62  extra_data_[extension->id()].notifications_disabled = notifications_disabled;
63}
64
65void InfoMap::RemoveExtension(const std::string& extension_id,
66                              const UnloadedExtensionInfo::Reason reason) {
67  CheckOnValidThread();
68  const Extension* extension = extensions_.GetByID(extension_id);
69  extra_data_.erase(extension_id);  // we don't care about disabled extra data
70  bool was_uninstalled = (reason != UnloadedExtensionInfo::REASON_DISABLE &&
71                          reason != UnloadedExtensionInfo::REASON_TERMINATE);
72  if (extension) {
73    if (!was_uninstalled)
74      disabled_extensions_.Insert(extension);
75    extensions_.Remove(extension_id);
76  } else if (was_uninstalled) {
77    // If the extension was uninstalled, make sure it's removed from the map of
78    // disabled extensions.
79    disabled_extensions_.Remove(extension_id);
80  } else {
81    // NOTE: This can currently happen if we receive multiple unload
82    // notifications, e.g. setting incognito-enabled state for a
83    // disabled extension (e.g., via sync).  See
84    // http://code.google.com/p/chromium/issues/detail?id=50582 .
85    NOTREACHED() << extension_id;
86  }
87}
88
89base::Time InfoMap::GetInstallTime(const std::string& extension_id) const {
90  ExtraDataMap::const_iterator iter = extra_data_.find(extension_id);
91  if (iter != extra_data_.end())
92    return iter->second.install_time;
93  return base::Time();
94}
95
96bool InfoMap::IsIncognitoEnabled(const std::string& extension_id) const {
97  // Keep in sync with duplicate in extensions/browser/process_manager.cc.
98  ExtraDataMap::const_iterator iter = extra_data_.find(extension_id);
99  if (iter != extra_data_.end())
100    return iter->second.incognito_enabled;
101  return false;
102}
103
104bool InfoMap::CanCrossIncognito(const Extension* extension) const {
105  // This is duplicated from ExtensionService :(.
106  return IsIncognitoEnabled(extension->id()) &&
107         !IncognitoInfo::IsSplitMode(extension);
108}
109
110void InfoMap::RegisterExtensionProcess(const std::string& extension_id,
111                                       int process_id,
112                                       int site_instance_id) {
113  if (!process_map_.Insert(extension_id, process_id, site_instance_id)) {
114    NOTREACHED() << "Duplicate extension process registration for: "
115                 << extension_id << "," << process_id << ".";
116  }
117}
118
119void InfoMap::UnregisterExtensionProcess(const std::string& extension_id,
120                                         int process_id,
121                                         int site_instance_id) {
122  if (!process_map_.Remove(extension_id, process_id, site_instance_id)) {
123    NOTREACHED() << "Unknown extension process registration for: "
124                 << extension_id << "," << process_id << ".";
125  }
126}
127
128void InfoMap::UnregisterAllExtensionsInProcess(int process_id) {
129  process_map_.RemoveAllFromProcess(process_id);
130}
131
132void InfoMap::GetExtensionsWithAPIPermissionForSecurityOrigin(
133    const GURL& origin,
134    int process_id,
135    APIPermission::ID permission,
136    ExtensionSet* extensions) const {
137  DCHECK(extensions);
138
139  if (origin.SchemeIs(kExtensionScheme)) {
140    const std::string& id = origin.host();
141    const Extension* extension = extensions_.GetByID(id);
142    if (extension &&
143        extension->permissions_data()->HasAPIPermission(permission) &&
144        process_map_.Contains(id, process_id)) {
145      extensions->Insert(extension);
146    }
147    return;
148  }
149
150  ExtensionSet::const_iterator i = extensions_.begin();
151  for (; i != extensions_.end(); ++i) {
152    if ((*i)->web_extent().MatchesSecurityOrigin(origin) &&
153        process_map_.Contains((*i)->id(), process_id) &&
154        (*i)->permissions_data()->HasAPIPermission(permission)) {
155      extensions->Insert(*i);
156    }
157  }
158}
159
160bool InfoMap::SecurityOriginHasAPIPermission(const GURL& origin,
161                                             int process_id,
162                                             APIPermission::ID permission)
163    const {
164  ExtensionSet extensions;
165  GetExtensionsWithAPIPermissionForSecurityOrigin(
166      origin, process_id, permission, &extensions);
167  return !extensions.is_empty();
168}
169
170// This function is security sensitive. Bugs could cause problems that break
171// restrictions on local file access or NaCl's validation caching. If you modify
172// this function, please get a security review from a NaCl person.
173bool InfoMap::MapUrlToLocalFilePath(const GURL& file_url,
174                                    bool use_blocking_api,
175                                    base::FilePath* file_path) {
176  // Check that the URL is recognized by the extension system.
177  const Extension* extension = extensions_.GetExtensionOrAppByURL(file_url);
178  if (!extension)
179    return false;
180
181  // This is a short-cut which avoids calling a blocking file operation
182  // (GetFilePath()), so that this can be called on the IO thread. It only
183  // handles a subset of the urls.
184  if (!use_blocking_api) {
185    if (file_url.SchemeIs(extensions::kExtensionScheme)) {
186      std::string path = file_url.path();
187      base::TrimString(path, "/", &path);  // Remove first slash
188      *file_path = extension->path().AppendASCII(path);
189      return true;
190    }
191    return false;
192  }
193
194  std::string path = file_url.path();
195  ExtensionResource resource;
196
197  if (SharedModuleInfo::IsImportedPath(path)) {
198    // Check if this is a valid path that is imported for this extension.
199    std::string new_extension_id;
200    std::string new_relative_path;
201    SharedModuleInfo::ParseImportedPath(
202        path, &new_extension_id, &new_relative_path);
203    const Extension* new_extension = extensions_.GetByID(new_extension_id);
204    if (!new_extension)
205      return false;
206
207    if (!SharedModuleInfo::ImportsExtensionById(extension, new_extension_id) ||
208        !SharedModuleInfo::IsExportAllowed(new_extension, new_relative_path)) {
209      return false;
210    }
211
212    resource = new_extension->GetResource(new_relative_path);
213  } else {
214    // Check that the URL references a resource in the extension.
215    resource = extension->GetResource(path);
216  }
217
218  if (resource.empty())
219    return false;
220
221  // GetFilePath is a blocking function call.
222  const base::FilePath resource_file_path = resource.GetFilePath();
223  if (resource_file_path.empty())
224    return false;
225
226  *file_path = resource_file_path;
227  return true;
228}
229
230QuotaService* InfoMap::GetQuotaService() {
231  CheckOnValidThread();
232  if (!quota_service_)
233    quota_service_.reset(new QuotaService());
234  return quota_service_.get();
235}
236
237void InfoMap::SetSigninProcess(int process_id) {
238  signin_process_id_ = process_id;
239}
240
241bool InfoMap::IsSigninProcess(int process_id) const {
242  return process_id == signin_process_id_;
243}
244
245void InfoMap::SetNotificationsDisabled(
246    const std::string& extension_id,
247    bool notifications_disabled) {
248  ExtraDataMap::iterator iter = extra_data_.find(extension_id);
249  if (iter != extra_data_.end())
250    iter->second.notifications_disabled = notifications_disabled;
251}
252
253bool InfoMap::AreNotificationsDisabled(
254    const std::string& extension_id) const {
255  ExtraDataMap::const_iterator iter = extra_data_.find(extension_id);
256  if (iter != extra_data_.end())
257    return iter->second.notifications_disabled;
258  return false;
259}
260
261void InfoMap::SetContentVerifier(ContentVerifier* verifier) {
262  content_verifier_ = verifier;
263}
264
265InfoMap::~InfoMap() {
266  if (quota_service_) {
267    BrowserThread::DeleteSoon(
268        BrowserThread::IO, FROM_HERE, quota_service_.release());
269  }
270}
271
272}  // namespace extensions
273