Home | History | Annotate | Download | only in renderer
      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 
     15 ScriptContextSet::ScriptContextSet() {
     16 }
     17 ScriptContextSet::~ScriptContextSet() {
     18 }
     19 
     20 int ScriptContextSet::size() const {
     21   return static_cast<int>(contexts_.size());
     22 }
     23 
     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 
     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 
     45 ScriptContextSet::ContextSet ScriptContextSet::GetAll() const {
     46   return contexts_;
     47 }
     48 
     49 ScriptContext* ScriptContextSet::GetCurrent() const {
     50   v8::Isolate* isolate = v8::Isolate::GetCurrent();
     51   return isolate->InContext() ? GetByV8Context(isolate->GetCurrentContext())
     52                               : NULL;
     53 }
     54 
     55 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 
     61 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 
     73 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 
    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