OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "extensions/renderer/script_context_set.h" | 5 #include "extensions/renderer/script_context_set.h" |
6 | 6 |
7 #include "base/message_loop/message_loop.h" | 7 #include "base/message_loop/message_loop.h" |
8 #include "content/public/common/url_constants.h" | |
8 #include "content/public/renderer/render_view.h" | 9 #include "content/public/renderer/render_view.h" |
9 #include "extensions/common/extension.h" | 10 #include "extensions/common/extension.h" |
11 #include "extensions/renderer/extension_groups.h" | |
10 #include "extensions/renderer/script_context.h" | 12 #include "extensions/renderer/script_context.h" |
13 #include "extensions/renderer/script_injection.h" | |
14 #include "third_party/WebKit/public/web/WebDocument.h" | |
15 #include "third_party/WebKit/public/web/WebLocalFrame.h" | |
11 #include "v8/include/v8.h" | 16 #include "v8/include/v8.h" |
12 | 17 |
13 namespace extensions { | 18 namespace extensions { |
14 | 19 |
15 ScriptContextSet::ScriptContextSet() { | 20 ScriptContextSet::ScriptContextSet(ExtensionSet* extensions, |
21 ExtensionIdSet* active_extension_ids) | |
22 : extensions_(extensions), active_extension_ids_(active_extension_ids) { | |
16 } | 23 } |
24 | |
17 ScriptContextSet::~ScriptContextSet() { | 25 ScriptContextSet::~ScriptContextSet() { |
18 } | 26 } |
19 | 27 |
20 int ScriptContextSet::size() const { | 28 ScriptContext* ScriptContextSet::Register( |
21 return static_cast<int>(contexts_.size()); | 29 blink::WebLocalFrame* frame, |
22 } | 30 const v8::Handle<v8::Context>& v8_context, |
31 int extension_group, | |
32 int world_id) { | |
33 const Extension* extension = | |
34 GetExtensionFromFrameAndWorld(frame, world_id, false); | |
35 const Extension* effective_extension = | |
36 GetExtensionFromFrameAndWorld(frame, world_id, true); | |
23 | 37 |
24 void ScriptContextSet::Add(ScriptContext* context) { | 38 GURL frame_url = ScriptContext::GetDataSourceURLForFrame(frame); |
25 #if DCHECK_IS_ON() | 39 Feature::Context context_type = |
26 // It's OK to insert the same context twice, but we should only ever have | 40 ClassifyJavaScriptContext(extension, extension_group, frame_url, |
27 // one ScriptContext per v8::Context. | 41 frame->document().securityOrigin()); |
28 for (ContextSet::iterator iter = contexts_.begin(); iter != contexts_.end(); | 42 Feature::Context effective_context_type = ClassifyJavaScriptContext( |
29 ++iter) { | 43 effective_extension, extension_group, |
30 ScriptContext* candidate = *iter; | 44 ScriptContext::GetEffectiveDocumentURL(frame, frame_url, true), |
31 if (candidate != context) | 45 frame->document().securityOrigin()); |
32 DCHECK(candidate->v8_context() != context->v8_context()); | 46 |
33 } | 47 ScriptContext* context = |
34 #endif | 48 new ScriptContext(v8_context, frame, extension, context_type, |
35 contexts_.insert(context); | 49 effective_extension, effective_context_type); |
50 contexts_.insert(context); // takes ownership | |
51 return context; | |
36 } | 52 } |
37 | 53 |
38 void ScriptContextSet::Remove(ScriptContext* context) { | 54 void ScriptContextSet::Remove(ScriptContext* context) { |
39 if (contexts_.erase(context)) { | 55 if (contexts_.erase(context)) { |
40 context->Invalidate(); | 56 context->Invalidate(); |
41 base::MessageLoop::current()->DeleteSoon(FROM_HERE, context); | 57 base::MessageLoop::current()->DeleteSoon(FROM_HERE, context); |
42 } | 58 } |
43 } | 59 } |
44 | 60 |
45 ScriptContextSet::ContextSet ScriptContextSet::GetAll() const { | |
46 return contexts_; | |
47 } | |
48 | |
49 ScriptContext* ScriptContextSet::GetCurrent() const { | 61 ScriptContext* ScriptContextSet::GetCurrent() const { |
50 v8::Isolate* isolate = v8::Isolate::GetCurrent(); | 62 v8::Isolate* isolate = v8::Isolate::GetCurrent(); |
51 return isolate->InContext() ? GetByV8Context(isolate->GetCurrentContext()) | 63 return isolate->InContext() ? GetByV8Context(isolate->GetCurrentContext()) |
52 : NULL; | 64 : nullptr; |
53 } | 65 } |
54 | 66 |
55 ScriptContext* ScriptContextSet::GetCalling() const { | 67 ScriptContext* ScriptContextSet::GetCalling() const { |
56 v8::Isolate* isolate = v8::Isolate::GetCurrent(); | 68 v8::Isolate* isolate = v8::Isolate::GetCurrent(); |
57 v8::Local<v8::Context> calling = isolate->GetCallingContext(); | 69 v8::Local<v8::Context> calling = isolate->GetCallingContext(); |
58 return calling.IsEmpty() ? NULL : GetByV8Context(calling); | 70 return calling.IsEmpty() ? nullptr : GetByV8Context(calling); |
59 } | 71 } |
60 | 72 |
61 ScriptContext* ScriptContextSet::GetByV8Context( | 73 ScriptContext* ScriptContextSet::GetByV8Context( |
62 v8::Handle<v8::Context> v8_context) const { | 74 const v8::Handle<v8::Context>& v8_context) const { |
63 for (ContextSet::const_iterator iter = contexts_.begin(); | 75 for (ScriptContext* script_context : contexts_) { |
64 iter != contexts_.end(); | 76 if (script_context->v8_context() == v8_context) |
65 ++iter) { | 77 return script_context; |
66 if ((*iter)->v8_context() == v8_context) | |
67 return *iter; | |
68 } | 78 } |
69 | 79 return nullptr; |
70 return NULL; | |
71 } | 80 } |
72 | 81 |
73 void ScriptContextSet::ForEach( | 82 void ScriptContextSet::ForEach( |
74 const std::string& extension_id, | 83 const std::string& extension_id, |
75 content::RenderView* render_view, | 84 content::RenderView* render_view, |
76 const base::Callback<void(ScriptContext*)>& callback) const { | 85 const base::Callback<void(ScriptContext*)>& callback) const { |
77 // We copy the context list, because calling into javascript may modify it | 86 // We copy the context list, because calling into javascript may modify it |
78 // out from under us. | 87 // out from under us. |
79 ContextSet contexts = GetAll(); | 88 std::set<ScriptContext*> contexts_copy = contexts_; |
80 | 89 |
81 for (ContextSet::iterator it = contexts.begin(); it != contexts.end(); ++it) { | 90 for (ScriptContext* context : contexts_copy) { |
82 ScriptContext* context = *it; | |
83 | |
84 // For the same reason as above, contexts may become invalid while we run. | 91 // For the same reason as above, contexts may become invalid while we run. |
85 if (!context->is_valid()) | 92 if (!context->is_valid()) |
86 continue; | 93 continue; |
87 | 94 |
88 if (!extension_id.empty()) { | 95 if (!extension_id.empty()) { |
89 const Extension* extension = context->extension(); | 96 const Extension* extension = context->extension(); |
90 if (!extension || (extension_id != extension->id())) | 97 if (!extension || (extension_id != extension->id())) |
91 continue; | 98 continue; |
92 } | 99 } |
93 | 100 |
94 content::RenderView* context_render_view = context->GetRenderView(); | 101 content::RenderView* context_render_view = context->GetRenderView(); |
95 if (!context_render_view) | 102 if (!context_render_view) |
96 continue; | 103 continue; |
97 | 104 |
98 if (render_view && render_view != context_render_view) | 105 if (render_view && render_view != context_render_view) |
99 continue; | 106 continue; |
100 | 107 |
101 callback.Run(context); | 108 callback.Run(context); |
102 } | 109 } |
103 } | 110 } |
104 | 111 |
105 ScriptContextSet::ContextSet ScriptContextSet::OnExtensionUnloaded( | 112 std::set<ScriptContext*> ScriptContextSet::OnExtensionUnloaded( |
106 const std::string& extension_id) { | 113 const std::string& extension_id) { |
107 ContextSet contexts = GetAll(); | 114 std::set<ScriptContext*> removed; |
108 ContextSet removed; | 115 ForEach(extension_id, |
109 | 116 base::Bind(&ScriptContextSet::DispatchOnUnloadEventAndRemove, |
110 // Clean up contexts belonging to the unloaded extension. This is done so | 117 base::Unretained(this), &removed)); |
111 // that content scripts (which remain injected into the page) don't continue | |
112 // receiving events and sending messages. | |
113 for (ContextSet::iterator it = contexts.begin(); it != contexts.end(); ++it) { | |
114 if ((*it)->extension() && (*it)->extension()->id() == extension_id) { | |
115 (*it)->DispatchOnUnloadEvent(); | |
116 removed.insert(*it); | |
117 Remove(*it); | |
118 } | |
119 } | |
120 | |
121 return removed; | 118 return removed; |
122 } | 119 } |
123 | 120 |
121 const Extension* ScriptContextSet::GetExtensionFromFrameAndWorld( | |
122 const blink::WebLocalFrame* frame, | |
123 int world_id, | |
124 bool use_effective_url) { | |
125 std::string extension_id; | |
126 if (world_id != 0) { | |
127 // Isolated worlds (content script). | |
128 extension_id = ScriptInjection::GetHostIdForIsolatedWorld(world_id); | |
129 } else if (!frame->document().securityOrigin().isUnique()) { | |
130 // TODO(kalman): Delete the above check. | |
131 // Extension pages (chrome-extension:// URLs). | |
132 GURL frame_url = ScriptContext::GetDataSourceURLForFrame(frame); | |
133 frame_url = ScriptContext::GetEffectiveDocumentURL(frame, frame_url, | |
134 use_effective_url); | |
135 extension_id = extensions_->GetExtensionOrAppIDByURL(frame_url); | |
136 } | |
137 | |
138 // There are conditions where despite a context being associated with an | |
139 // extension, no extension actually gets found. Ignore "invalid" because CSP | |
140 // blocks extension page loading by switching the extension ID to "invalid". | |
141 const Extension* extension = extensions_->GetByID(extension_id); | |
142 if (!extension && !extension_id.empty() && extension_id != "invalid") { | |
143 // TODO(kalman): Do something here? | |
Ken Rockot(use gerrit already)
2015/03/12 22:45:16
Unless I'm reading this wrong, is this no longer r
not at google - send to devlin
2015/03/12 22:48:34
Yes, I added that UMA maybe 4 years ago? It was ne
| |
144 } | |
145 return extension; | |
146 } | |
147 | |
148 Feature::Context ScriptContextSet::ClassifyJavaScriptContext( | |
149 const Extension* extension, | |
150 int extension_group, | |
151 const GURL& url, | |
152 const blink::WebSecurityOrigin& origin) { | |
153 // WARNING: This logic must match ProcessMap::GetContextType, as much as | |
154 // possible. | |
155 | |
156 DCHECK_GE(extension_group, 0); | |
157 if (extension_group == EXTENSION_GROUP_CONTENT_SCRIPTS) { | |
158 return extension ? // TODO(kalman): when does this happen? | |
159 Feature::CONTENT_SCRIPT_CONTEXT | |
160 : Feature::UNSPECIFIED_CONTEXT; | |
161 } | |
162 | |
163 // We have an explicit check for sandboxed pages before checking whether the | |
164 // extension is active in this process because: | |
165 // 1. Sandboxed pages run in the same process as regular extension pages, so | |
166 // the extension is considered active. | |
167 // 2. ScriptContext creation (which triggers bindings injection) happens | |
168 // before the SecurityContext is updated with the sandbox flags (after | |
169 // reading the CSP header), so the caller can't check if the context's | |
170 // security origin is unique yet. | |
171 if (ScriptContext::IsSandboxedPage(*extensions_, url)) | |
172 return Feature::WEB_PAGE_CONTEXT; | |
173 | |
174 if (extension && active_extension_ids_->count(extension->id()) > 0) { | |
175 // |extension| is active in this process, but it could be either a true | |
176 // extension process or within the extent of a hosted app. In the latter | |
177 // case this would usually be considered a (blessed) web page context, | |
178 // unless the extension in question is a component extension, in which case | |
179 // we cheat and call it blessed. | |
180 return (extension->is_hosted_app() && | |
181 extension->location() != Manifest::COMPONENT) | |
182 ? Feature::BLESSED_WEB_PAGE_CONTEXT | |
183 : Feature::BLESSED_EXTENSION_CONTEXT; | |
184 } | |
185 | |
186 // TODO(kalman): This isUnique() check is wrong, it should be performed as | |
187 // part of ScriptContext::IsSandboxedPage(). | |
188 if (!origin.isUnique() && extensions_->ExtensionBindingsAllowed(url)) { | |
189 if (!extension) // TODO(kalman): when does this happen? | |
190 return Feature::UNSPECIFIED_CONTEXT; | |
191 return extension->is_hosted_app() ? Feature::BLESSED_WEB_PAGE_CONTEXT | |
192 : Feature::UNBLESSED_EXTENSION_CONTEXT; | |
193 } | |
194 | |
195 if (!url.is_valid()) | |
196 return Feature::UNSPECIFIED_CONTEXT; | |
197 | |
198 if (url.SchemeIs(content::kChromeUIScheme)) | |
199 return Feature::WEBUI_CONTEXT; | |
200 | |
201 return Feature::WEB_PAGE_CONTEXT; | |
202 } | |
203 | |
204 void ScriptContextSet::DispatchOnUnloadEventAndRemove( | |
205 std::set<ScriptContext*>* out, | |
206 ScriptContext* context) { | |
207 Remove(context); // deleted asynchronously | |
208 out->insert(context); | |
209 } | |
210 | |
124 } // namespace extensions | 211 } // namespace extensions |
OLD | NEW |