• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/script_context_set.h"
6 
7 #include "base/message_loop/message_loop.h"
8 #include "content/public/renderer/render_view.h"
9 #include "extensions/common/extension.h"
10 #include "extensions/renderer/script_context.h"
11 #include "v8/include/v8.h"
12 
13 namespace extensions {
14 
ScriptContextSet()15 ScriptContextSet::ScriptContextSet() {
16 }
~ScriptContextSet()17 ScriptContextSet::~ScriptContextSet() {
18 }
19 
size() const20 int ScriptContextSet::size() const {
21   return static_cast<int>(contexts_.size());
22 }
23 
Add(ScriptContext * context)24 void ScriptContextSet::Add(ScriptContext* context) {
25 #if DCHECK_IS_ON
26   // It's OK to insert the same context twice, but we should only ever have
27   // one ScriptContext per v8::Context.
28   for (ContextSet::iterator iter = contexts_.begin(); iter != contexts_.end();
29        ++iter) {
30     ScriptContext* candidate = *iter;
31     if (candidate != context)
32       DCHECK(candidate->v8_context() != context->v8_context());
33   }
34 #endif
35   contexts_.insert(context);
36 }
37 
Remove(ScriptContext * context)38 void ScriptContextSet::Remove(ScriptContext* context) {
39   if (contexts_.erase(context)) {
40     context->Invalidate();
41     base::MessageLoop::current()->DeleteSoon(FROM_HERE, context);
42   }
43 }
44 
GetAll() const45 ScriptContextSet::ContextSet ScriptContextSet::GetAll() const {
46   return contexts_;
47 }
48 
GetCurrent() const49 ScriptContext* ScriptContextSet::GetCurrent() const {
50   v8::Isolate* isolate = v8::Isolate::GetCurrent();
51   return isolate->InContext() ? GetByV8Context(isolate->GetCurrentContext())
52                               : NULL;
53 }
54 
GetCalling() const55 ScriptContext* ScriptContextSet::GetCalling() const {
56   v8::Isolate* isolate = v8::Isolate::GetCurrent();
57   v8::Local<v8::Context> calling = isolate->GetCallingContext();
58   return calling.IsEmpty() ? NULL : GetByV8Context(calling);
59 }
60 
GetByV8Context(v8::Handle<v8::Context> v8_context) const61 ScriptContext* ScriptContextSet::GetByV8Context(
62     v8::Handle<v8::Context> v8_context) const {
63   for (ContextSet::const_iterator iter = contexts_.begin();
64        iter != contexts_.end();
65        ++iter) {
66     if ((*iter)->v8_context() == v8_context)
67       return *iter;
68   }
69 
70   return NULL;
71 }
72 
ForEach(const std::string & extension_id,content::RenderView * render_view,const base::Callback<void (ScriptContext *)> & callback) const73 void ScriptContextSet::ForEach(
74     const std::string& extension_id,
75     content::RenderView* render_view,
76     const base::Callback<void(ScriptContext*)>& callback) const {
77   // We copy the context list, because calling into javascript may modify it
78   // out from under us.
79   ContextSet contexts = GetAll();
80 
81   for (ContextSet::iterator it = contexts.begin(); it != contexts.end(); ++it) {
82     ScriptContext* context = *it;
83 
84     // For the same reason as above, contexts may become invalid while we run.
85     if (!context->is_valid())
86       continue;
87 
88     if (!extension_id.empty()) {
89       const Extension* extension = context->extension();
90       if (!extension || (extension_id != extension->id()))
91         continue;
92     }
93 
94     content::RenderView* context_render_view = context->GetRenderView();
95     if (!context_render_view)
96       continue;
97 
98     if (render_view && render_view != context_render_view)
99       continue;
100 
101     callback.Run(context);
102   }
103 }
104 
OnExtensionUnloaded(const std::string & extension_id)105 ScriptContextSet::ContextSet ScriptContextSet::OnExtensionUnloaded(
106     const std::string& extension_id) {
107   ContextSet contexts = GetAll();
108   ContextSet removed;
109 
110   // Clean up contexts belonging to the unloaded extension. This is done so
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;
122 }
123 
124 }  // namespace extensions
125