1// Copyright (c) 2012 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 "chrome/renderer/extensions/chrome_v8_context_set.h"
6
7#include "base/logging.h"
8#include "base/message_loop/message_loop.h"
9#include "base/tracked_objects.h"
10#include "base/values.h"
11#include "chrome/common/extensions/extension.h"
12#include "chrome/common/url_constants.h"
13#include "chrome/renderer/extensions/chrome_v8_context.h"
14#include "content/public/renderer/render_thread.h"
15#include "content/public/renderer/render_view.h"
16#include "extensions/common/constants.h"
17#include "third_party/WebKit/public/platform/WebURL.h"
18#include "third_party/WebKit/public/platform/WebURLRequest.h"
19#include "third_party/WebKit/public/web/WebDocument.h"
20#include "third_party/WebKit/public/web/WebFrame.h"
21#include "third_party/WebKit/public/web/WebView.h"
22#include "v8/include/v8.h"
23
24using content::RenderThread;
25
26namespace extensions {
27
28ChromeV8ContextSet::ChromeV8ContextSet() {
29}
30ChromeV8ContextSet::~ChromeV8ContextSet() {
31}
32
33int ChromeV8ContextSet::size() const {
34  return static_cast<int>(contexts_.size());
35}
36
37void ChromeV8ContextSet::Add(ChromeV8Context* context) {
38  if (DCHECK_IS_ON()) {
39    // It's OK to insert the same context twice, but we should only ever have
40    // one ChromeV8Context per v8::Context.
41    for (ContextSet::iterator iter = contexts_.begin(); iter != contexts_.end();
42        ++iter) {
43      ChromeV8Context* candidate = *iter;
44      if (candidate != context)
45        DCHECK(candidate->v8_context() != context->v8_context());
46    }
47  }
48  contexts_.insert(context);
49}
50
51void ChromeV8ContextSet::Remove(ChromeV8Context* context) {
52  if (contexts_.erase(context)) {
53    context->Invalidate();
54    base::MessageLoop::current()->DeleteSoon(FROM_HERE, context);
55  }
56}
57
58ChromeV8ContextSet::ContextSet ChromeV8ContextSet::GetAll() const {
59  return contexts_;
60}
61
62ChromeV8Context* ChromeV8ContextSet::GetCurrent() const {
63  return v8::Context::InContext() ?
64      GetByV8Context(v8::Context::GetCurrent()) : NULL;
65}
66
67ChromeV8Context* ChromeV8ContextSet::GetCalling() const {
68  v8::Local<v8::Context> calling = v8::Context::GetCalling();
69  return calling.IsEmpty() ? NULL : GetByV8Context(calling);
70}
71
72ChromeV8Context* ChromeV8ContextSet::GetByV8Context(
73    v8::Handle<v8::Context> v8_context) const {
74  for (ContextSet::const_iterator iter = contexts_.begin();
75       iter != contexts_.end(); ++iter) {
76    if ((*iter)->v8_context() == v8_context)
77      return *iter;
78  }
79
80  return NULL;
81}
82
83void ChromeV8ContextSet::ForEach(
84    const std::string& extension_id,
85    content::RenderView* render_view,
86    const base::Callback<void(ChromeV8Context*)>& callback) const {
87  // We copy the context list, because calling into javascript may modify it
88  // out from under us.
89  ContextSet contexts = GetAll();
90
91  for (ContextSet::iterator it = contexts.begin(); it != contexts.end();
92       ++it) {
93    ChromeV8Context* context = *it;
94
95    // For the same reason as above, contexts may become invalid while we run.
96    if (!context->is_valid())
97      continue;
98
99    if (!extension_id.empty()) {
100      const Extension* extension = context->extension();
101      if (!extension || (extension_id != extension->id()))
102        continue;
103    }
104
105    content::RenderView* context_render_view = context->GetRenderView();
106    if (!context_render_view)
107      continue;
108
109    if (render_view && render_view != context_render_view)
110      continue;
111
112    callback.Run(context);
113  }
114}
115
116ChromeV8ContextSet::ContextSet ChromeV8ContextSet::OnExtensionUnloaded(
117    const std::string& extension_id) {
118  ContextSet contexts = GetAll();
119  ContextSet removed;
120
121  // Clean up contexts belonging to the unloaded extension. This is done so
122  // that content scripts (which remain injected into the page) don't continue
123  // receiving events and sending messages.
124  for (ContextSet::iterator it = contexts.begin(); it != contexts.end();
125       ++it) {
126    if ((*it)->extension() && (*it)->extension()->id() == extension_id) {
127      (*it)->DispatchOnUnloadEvent();
128      removed.insert(*it);
129      Remove(*it);
130    }
131  }
132
133  return removed;
134}
135
136}  // namespace extensions
137