1// Copyright 2014 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/renderer/user_script_set_manager.h"
6
7#include "components/crx_file/id_util.h"
8#include "content/public/renderer/render_thread.h"
9#include "extensions/common/extension_messages.h"
10#include "extensions/renderer/dispatcher.h"
11#include "extensions/renderer/script_injection.h"
12#include "extensions/renderer/user_script_set.h"
13#include "ipc/ipc_message.h"
14#include "ipc/ipc_message_macros.h"
15#include "third_party/WebKit/public/web/WebFrame.h"
16
17namespace extensions {
18
19UserScriptSetManager::UserScriptSetManager(const ExtensionSet* extensions)
20    : static_scripts_(extensions), extensions_(extensions) {
21  content::RenderThread::Get()->AddObserver(this);
22}
23
24UserScriptSetManager::~UserScriptSetManager() {
25}
26
27void UserScriptSetManager::AddObserver(Observer* observer) {
28  observers_.AddObserver(observer);
29}
30
31void UserScriptSetManager::RemoveObserver(Observer* observer) {
32  observers_.RemoveObserver(observer);
33}
34
35scoped_ptr<ScriptInjection>
36UserScriptSetManager::GetInjectionForDeclarativeScript(
37    int script_id,
38    blink::WebFrame* web_frame,
39    int tab_id,
40    const GURL& url,
41    const Extension* extension) {
42  UserScriptSet* user_script_set =
43      GetProgrammaticScriptsByExtension(extension->id());
44  if (!user_script_set)
45    return scoped_ptr<ScriptInjection>();
46
47  return user_script_set->GetDeclarativeScriptInjection(
48      script_id,
49      web_frame,
50      tab_id,
51      UserScript::BROWSER_DRIVEN,
52      url,
53      extension);
54}
55
56bool UserScriptSetManager::OnControlMessageReceived(
57    const IPC::Message& message) {
58  bool handled = true;
59  IPC_BEGIN_MESSAGE_MAP(UserScriptSetManager, message)
60    IPC_MESSAGE_HANDLER(ExtensionMsg_UpdateUserScripts, OnUpdateUserScripts)
61    IPC_MESSAGE_UNHANDLED(handled = false)
62  IPC_END_MESSAGE_MAP()
63  return handled;
64}
65
66void UserScriptSetManager::GetAllInjections(
67    ScopedVector<ScriptInjection>* injections,
68    blink::WebFrame* web_frame,
69    int tab_id,
70    UserScript::RunLocation run_location) {
71  static_scripts_.GetInjections(injections, web_frame, tab_id, run_location);
72  for (UserScriptSetMap::iterator it = programmatic_scripts_.begin();
73       it != programmatic_scripts_.end();
74       ++it) {
75    it->second->GetInjections(injections, web_frame, tab_id, run_location);
76  }
77}
78
79void UserScriptSetManager::GetAllActiveExtensionIds(
80    std::set<std::string>* ids) const {
81  DCHECK(ids);
82  static_scripts_.GetActiveExtensionIds(ids);
83  for (UserScriptSetMap::const_iterator it = programmatic_scripts_.begin();
84       it != programmatic_scripts_.end();
85       ++it) {
86    it->second->GetActiveExtensionIds(ids);
87  }
88}
89
90UserScriptSet* UserScriptSetManager::GetProgrammaticScriptsByExtension(
91    const ExtensionId& extension_id) {
92  UserScriptSetMap::const_iterator it =
93      programmatic_scripts_.find(extension_id);
94  return it != programmatic_scripts_.end() ? it->second.get() : NULL;
95}
96
97void UserScriptSetManager::OnUpdateUserScripts(
98    base::SharedMemoryHandle shared_memory,
99    const ExtensionId& extension_id,
100    const std::set<std::string>& changed_extensions) {
101  if (!base::SharedMemory::IsHandleValid(shared_memory)) {
102    NOTREACHED() << "Bad scripts handle";
103    return;
104  }
105
106  for (std::set<std::string>::const_iterator iter = changed_extensions.begin();
107       iter != changed_extensions.end();
108       ++iter) {
109    if (!crx_file::id_util::IdIsValid(*iter)) {
110      NOTREACHED() << "Invalid extension id: " << *iter;
111      return;
112    }
113  }
114
115  UserScriptSet* scripts = NULL;
116  if (!extension_id.empty()) {
117    // The expectation when there is an extension that "owns" this shared
118    // memory region is that the |changed_extensions| is either the empty list
119    // or just the owner.
120    CHECK(changed_extensions.size() <= 1);
121    if (programmatic_scripts_.find(extension_id) ==
122        programmatic_scripts_.end()) {
123      scripts = new UserScriptSet(extensions_);
124      programmatic_scripts_[extension_id] = make_linked_ptr(scripts);
125    } else {
126      scripts = programmatic_scripts_[extension_id].get();
127    }
128  } else {
129    scripts = &static_scripts_;
130  }
131  DCHECK(scripts);
132
133  // If no extensions are included in the set, that indicates that all
134  // extensions were updated. Add them all to the set so that observers and
135  // individual UserScriptSets don't need to know this detail.
136  const std::set<std::string>* effective_extensions = &changed_extensions;
137  std::set<std::string> all_extensions;
138  if (changed_extensions.empty()) {
139    // The meaning of "all extensions" varies, depending on whether some
140    // extension "owns" this shared memory region.
141    // No owner => all known extensions.
142    // Owner    => just the owner extension.
143    if (extension_id.empty())
144      all_extensions = extensions_->GetIDs();
145    else
146      all_extensions.insert(extension_id);
147    effective_extensions = &all_extensions;
148  }
149
150  if (scripts->UpdateUserScripts(shared_memory, *effective_extensions)) {
151    FOR_EACH_OBSERVER(
152        Observer,
153        observers_,
154        OnUserScriptsUpdated(*effective_extensions, scripts->scripts()));
155  }
156}
157
158}  // namespace extensions
159