summaryrefslogtreecommitdiffstats
path: root/extensions/renderer/script_context_set.cc
blob: 726827b2e96790f1f503b07dfe35adeb53ae6063 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "extensions/renderer/script_context_set.h"

#include "base/debug/alias.h"
#include "base/debug/dump_without_crashing.h"
#include "base/message_loop/message_loop.h"
#include "content/public/renderer/render_view.h"
#include "extensions/common/extension.h"
#include "extensions/renderer/script_context.h"
#include "v8/include/v8.h"

namespace extensions {

ScriptContextSet::ScriptContextSet() {
}
ScriptContextSet::~ScriptContextSet() {
}

int ScriptContextSet::size() const {
  return static_cast<int>(contexts_.size());
}

void ScriptContextSet::Add(ScriptContext* context) {
#if DCHECK_IS_ON()
  // It's OK to insert the same context twice, but we should only ever have
  // one ScriptContext per v8::Context.
  for (ContextSet::iterator iter = contexts_.begin(); iter != contexts_.end();
       ++iter) {
    ScriptContext* candidate = *iter;
    if (candidate != context)
      DCHECK(candidate->v8_context() != context->v8_context());
  }
#endif
  contexts_.insert(context);
}

void ScriptContextSet::Remove(ScriptContext* context) {
  if (contexts_.erase(context)) {
    context->Invalidate();
    base::MessageLoop::current()->DeleteSoon(FROM_HERE, context);
  }
}

void ScriptContextSet::RemoveForFrame(blink::WebFrame* frame) {
  // It is a major problem if there are any remaining contexts associated with a
  // WebFrame is about to be detached. It is too late to fire the extension
  // onunload event, and the ScriptContext will try to use a WebFrame after it
  // may have been freed.
  static const int kMaxWorldIdsToSave = 10;
  int saved_world_ids_count = 0;
  int saved_world_ids[kMaxWorldIdsToSave] = {};
  for (ContextSet::iterator iter = contexts_.begin();
       iter != contexts_.end();) {
    ScriptContext* context = *iter++;
    if (context->web_frame() == frame) {
      if (saved_world_ids_count < kMaxWorldIdsToSave)
        saved_world_ids[saved_world_ids_count++] = context->world_id();
      Remove(context);
    }
  }
#if !defined(OS_LINUX)
  // DumpWithoutCrashing() crashes in Linux in renderers with breakpad +
  // sandboxing. https://crbug.com/349600
  base::debug::Alias(&saved_world_ids_count);
  base::debug::Alias(saved_world_ids);
  base::debug::DumpWithoutCrashing();
#endif
}

ScriptContextSet::ContextSet ScriptContextSet::GetAll() const {
  return contexts_;
}

ScriptContext* ScriptContextSet::GetCurrent() const {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  return isolate->InContext() ? GetByV8Context(isolate->GetCurrentContext())
                              : NULL;
}

ScriptContext* ScriptContextSet::GetCalling() const {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::Local<v8::Context> calling = isolate->GetCallingContext();
  return calling.IsEmpty() ? NULL : GetByV8Context(calling);
}

ScriptContext* ScriptContextSet::GetByV8Context(
    v8::Handle<v8::Context> v8_context) const {
  for (ContextSet::const_iterator iter = contexts_.begin();
       iter != contexts_.end();
       ++iter) {
    if ((*iter)->v8_context() == v8_context)
      return *iter;
  }

  return NULL;
}

void ScriptContextSet::ForEach(
    const std::string& extension_id,
    content::RenderView* render_view,
    const base::Callback<void(ScriptContext*)>& callback) const {
  // We copy the context list, because calling into javascript may modify it
  // out from under us.
  ContextSet contexts = GetAll();

  for (ContextSet::iterator it = contexts.begin(); it != contexts.end(); ++it) {
    ScriptContext* context = *it;

    // For the same reason as above, contexts may become invalid while we run.
    if (!context->is_valid())
      continue;

    if (!extension_id.empty()) {
      const Extension* extension = context->extension();
      if (!extension || (extension_id != extension->id()))
        continue;
    }

    content::RenderView* context_render_view = context->GetRenderView();
    if (!context_render_view)
      continue;

    if (render_view && render_view != context_render_view)
      continue;

    callback.Run(context);
  }
}

ScriptContextSet::ContextSet ScriptContextSet::OnExtensionUnloaded(
    const std::string& extension_id) {
  ContextSet contexts = GetAll();
  ContextSet removed;

  // Clean up contexts belonging to the unloaded extension. This is done so
  // that content scripts (which remain injected into the page) don't continue
  // receiving events and sending messages.
  for (ContextSet::iterator it = contexts.begin(); it != contexts.end(); ++it) {
    if ((*it)->extension() && (*it)->extension()->id() == extension_id) {
      (*it)->DispatchOnUnloadEvent();
      removed.insert(*it);
      Remove(*it);
    }
  }

  return removed;
}

}  // namespace extensions