Revert 4 patches that deal with Extension ScriptContexts that have been submitted very recently.

There are high crash rates caused by invalid ScriptContexts and it's unclear
which of these patches is causing them - potentially all of them. Reverting all
to be safe, and because they stomped on each other and can't be reverted
separately.

Revert "Move Extension ScriptContext creation into ScriptContextSet."

This reverts commit 745b66237eca67b22225941eb475ba8a995e1287.

Revert "[Extensions] extension sendMessage respect to ActiveDOMObject policy"

This reverts commit 441ece58d63d2bf6e1f82e4055c71ef2f2ef63f4.

Revert "Add back the call to extensions::ScriptContext::DispatchOnUnloadEvent."

This reverts commit 0eda971a264a38756cc07416b1d1943b7e324aca.

Revert "[Extensions] Fix crash in ScriptContextSet"

This reverts commit a08a3109cc17941e39eb54b4d4223cfc14de83ae.

BUG=471599
[email protected]

Review URL: https://codereview.chromium.org/1064473003

Cr-Commit-Position: refs/heads/master@{#323869}
diff --git a/extensions/renderer/script_context_set.cc b/extensions/renderer/script_context_set.cc
index 5ac4610..7b26b4e 100644
--- a/extensions/renderer/script_context_set.cc
+++ b/extensions/renderer/script_context_set.cc
@@ -5,266 +5,81 @@
 #include "extensions/renderer/script_context_set.h"
 
 #include "base/message_loop/message_loop.h"
-#include "content/public/common/url_constants.h"
-#include "content/public/renderer/render_frame.h"
-#include "content/public/renderer/render_frame_observer.h"
 #include "content/public/renderer/render_view.h"
 #include "extensions/common/extension.h"
-#include "extensions/renderer/extension_groups.h"
 #include "extensions/renderer/script_context.h"
-#include "extensions/renderer/script_injection.h"
-#include "third_party/WebKit/public/platform/WebSuspendableTask.h"
-#include "third_party/WebKit/public/web/WebDocument.h"
-#include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "v8/include/v8.h"
 
 namespace extensions {
 
-namespace {
-
-// This class inherits content::RenderFrameObserver for tracking when render
-// frame goes away.
-class BlinkTaskRunner : public blink::WebSuspendableTask,
-                        public content::RenderFrameObserver {
- public:
-  BlinkTaskRunner(content::RenderFrame* render_frame,
-                  ScriptContext* context,
-                  const base::Callback<void(ScriptContext*)>& callback)
-      : RenderFrameObserver(render_frame),
-        context_(context),
-        callback_(callback) {}
-
-  void run() override {
-    if (render_frame() && context_->is_valid())
-      callback_.Run(context_);
-  }
-
- private:
-  ScriptContext* context_;
-  base::Callback<void(ScriptContext*)> callback_;
-
-  // Overriden to avoid being destroyed when RenderFrame goes away.
-  // BlinkTaskRunner objects are owned by WebLocalFrame.
-  void OnDestruct() override {}
-
-  DISALLOW_COPY_AND_ASSIGN(BlinkTaskRunner);
-};
-
-// This class deletes ScriptContext on run or on contextDestoyed.
-// It allows us to pass ScriptContext as raw pointer to BlinkTaskRunner.
-// In ScriptContextSet::Remove method we mark the context as invalid, but
-// don't delete the object until all scheduled tasks are finished.
-class ScriptContextDeleter : public blink::WebSuspendableTask {
- public:
-  explicit ScriptContextDeleter(ScriptContext* context) : context_(context) {}
-
-  void run() override { delete context_; }
-  void contextDestroyed() override { delete context_; }
-
- private:
-  ScriptContext* context_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScriptContextDeleter);
-};
-
-}  // namespace
-
-ScriptContextSet::ScriptContextSet(ExtensionSet* extensions,
-                                   ExtensionIdSet* active_extension_ids)
-    : extensions_(extensions), active_extension_ids_(active_extension_ids) {
+ScriptContextSet::ScriptContextSet() {
 }
-
 ScriptContextSet::~ScriptContextSet() {
 }
 
-ScriptContext* ScriptContextSet::Register(
-    blink::WebLocalFrame* frame,
-    const v8::Handle<v8::Context>& v8_context,
-    int extension_group,
-    int world_id) {
-  const Extension* extension =
-      GetExtensionFromFrameAndWorld(frame, world_id, false);
-  const Extension* effective_extension =
-      GetExtensionFromFrameAndWorld(frame, world_id, true);
+int ScriptContextSet::size() const {
+  return static_cast<int>(contexts_.size());
+}
 
-  GURL frame_url = ScriptContext::GetDataSourceURLForFrame(frame);
-  Feature::Context context_type =
-      ClassifyJavaScriptContext(extension, extension_group, frame_url,
-                                frame->document().securityOrigin());
-  Feature::Context effective_context_type = ClassifyJavaScriptContext(
-      effective_extension, extension_group,
-      ScriptContext::GetEffectiveDocumentURL(frame, frame_url, true),
-      frame->document().securityOrigin());
-
-  ScriptContext* context =
-      new ScriptContext(v8_context, frame, extension, context_type,
-                        effective_extension, effective_context_type);
-  contexts_.insert(context);  // takes ownership
-  return context;
+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)) {
-    content::RenderFrame* context_render_frame = context->GetRenderFrame();
-    blink::WebLocalFrame* web_local_frame =
-        context_render_frame ? context_render_frame->GetWebFrame() : nullptr;
-
     context->Invalidate();
-
-    if (!web_local_frame)
-      base::MessageLoop::current()->DeleteSoon(FROM_HERE, context);
-    else
-      web_local_frame->requestRunTask(new ScriptContextDeleter(context));
+    base::MessageLoop::current()->DeleteSoon(FROM_HERE, context);
   }
 }
 
+ScriptContextSet::ContextSet ScriptContextSet::GetAll() const {
+  return contexts_;
+}
+
 ScriptContext* ScriptContextSet::GetCurrent() const {
   v8::Isolate* isolate = v8::Isolate::GetCurrent();
   return isolate->InContext() ? GetByV8Context(isolate->GetCurrentContext())
-                              : nullptr;
+                              : NULL;
 }
 
 ScriptContext* ScriptContextSet::GetCalling() const {
   v8::Isolate* isolate = v8::Isolate::GetCurrent();
   v8::Local<v8::Context> calling = isolate->GetCallingContext();
-  return calling.IsEmpty() ? nullptr : GetByV8Context(calling);
+  return calling.IsEmpty() ? NULL : GetByV8Context(calling);
 }
 
 ScriptContext* ScriptContextSet::GetByV8Context(
-    const v8::Handle<v8::Context>& v8_context) const {
-  for (ScriptContext* script_context : contexts_) {
-    if (script_context->v8_context() == v8_context)
-      return script_context;
+    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 nullptr;
+
+  return NULL;
 }
 
 void ScriptContextSet::ForEach(
     const std::string& extension_id,
-    content::RenderView* restrict_to_render_view,
+    content::RenderView* render_view,
     const base::Callback<void(ScriptContext*)>& callback) const {
-  ForEachImpl(extension_id, restrict_to_render_view, callback, false);
-}
-
-void ScriptContextSet::RequestRunForEach(
-    const std::string& extension_id,
-    content::RenderFrame* restrict_to_render_frame,
-    const base::Callback<void(ScriptContext*)>& callback) const {
-  content::RenderView* restrict_to_render_view =
-      restrict_to_render_frame ? restrict_to_render_frame->GetRenderView()
-                               : NULL;
-  ForEachImpl(extension_id, restrict_to_render_view, callback, true);
-}
-
-std::set<ScriptContext*> ScriptContextSet::OnExtensionUnloaded(
-    const std::string& extension_id) {
-  std::set<ScriptContext*> removed;
-  ForEach(extension_id,
-          base::Bind(&ScriptContextSet::DispatchOnUnloadEventAndRemove,
-                     base::Unretained(this), &removed));
-  return removed;
-}
-
-const Extension* ScriptContextSet::GetExtensionFromFrameAndWorld(
-    const blink::WebLocalFrame* frame,
-    int world_id,
-    bool use_effective_url) {
-  std::string extension_id;
-  if (world_id != 0) {
-    // Isolated worlds (content script).
-    extension_id = ScriptInjection::GetHostIdForIsolatedWorld(world_id);
-  } else if (!frame->document().securityOrigin().isUnique()) {
-    // TODO(kalman): Delete the above check.
-    // Extension pages (chrome-extension:// URLs).
-    GURL frame_url = ScriptContext::GetDataSourceURLForFrame(frame);
-    frame_url = ScriptContext::GetEffectiveDocumentURL(frame, frame_url,
-                                                       use_effective_url);
-    extension_id = extensions_->GetExtensionOrAppIDByURL(frame_url);
-  }
-
-  // There are conditions where despite a context being associated with an
-  // extension, no extension actually gets found. Ignore "invalid" because CSP
-  // blocks extension page loading by switching the extension ID to "invalid".
-  const Extension* extension = extensions_->GetByID(extension_id);
-  if (!extension && !extension_id.empty() && extension_id != "invalid") {
-    // TODO(kalman): Do something here?
-  }
-  return extension;
-}
-
-Feature::Context ScriptContextSet::ClassifyJavaScriptContext(
-    const Extension* extension,
-    int extension_group,
-    const GURL& url,
-    const blink::WebSecurityOrigin& origin) {
-  // WARNING: This logic must match ProcessMap::GetContextType, as much as
-  // possible.
-
-  DCHECK_GE(extension_group, 0);
-  if (extension_group == EXTENSION_GROUP_CONTENT_SCRIPTS) {
-    return extension ?  // TODO(kalman): when does this happen?
-               Feature::CONTENT_SCRIPT_CONTEXT
-                     : Feature::UNSPECIFIED_CONTEXT;
-  }
-
-  // We have an explicit check for sandboxed pages before checking whether the
-  // extension is active in this process because:
-  // 1. Sandboxed pages run in the same process as regular extension pages, so
-  //    the extension is considered active.
-  // 2. ScriptContext creation (which triggers bindings injection) happens
-  //    before the SecurityContext is updated with the sandbox flags (after
-  //    reading the CSP header), so the caller can't check if the context's
-  //    security origin is unique yet.
-  if (ScriptContext::IsSandboxedPage(*extensions_, url))
-    return Feature::WEB_PAGE_CONTEXT;
-
-  if (extension && active_extension_ids_->count(extension->id()) > 0) {
-    // |extension| is active in this process, but it could be either a true
-    // extension process or within the extent of a hosted app. In the latter
-    // case this would usually be considered a (blessed) web page context,
-    // unless the extension in question is a component extension, in which case
-    // we cheat and call it blessed.
-    return (extension->is_hosted_app() &&
-            extension->location() != Manifest::COMPONENT)
-               ? Feature::BLESSED_WEB_PAGE_CONTEXT
-               : Feature::BLESSED_EXTENSION_CONTEXT;
-  }
-
-  // TODO(kalman): This isUnique() check is wrong, it should be performed as
-  // part of ScriptContext::IsSandboxedPage().
-  if (!origin.isUnique() && extensions_->ExtensionBindingsAllowed(url)) {
-    if (!extension)  // TODO(kalman): when does this happen?
-      return Feature::UNSPECIFIED_CONTEXT;
-    return extension->is_hosted_app() ? Feature::BLESSED_WEB_PAGE_CONTEXT
-                                      : Feature::UNBLESSED_EXTENSION_CONTEXT;
-  }
-
-  if (!url.is_valid())
-    return Feature::UNSPECIFIED_CONTEXT;
-
-  if (url.SchemeIs(content::kChromeUIScheme))
-    return Feature::WEBUI_CONTEXT;
-
-  return Feature::WEB_PAGE_CONTEXT;
-}
-
-void ScriptContextSet::DispatchOnUnloadEventAndRemove(
-    std::set<ScriptContext*>* out,
-    ScriptContext* context) {
-  context->DispatchOnUnloadEvent();
-  Remove(context);  // deleted asynchronously
-  out->insert(context);
-}
-
-void ScriptContextSet::ForEachImpl(
-    const std::string& extension_id,
-    content::RenderView* restrict_to_render_view,
-    const base::Callback<void(ScriptContext*)>& callback,
-    bool run_asynchronously) const {
   // We copy the context list, because calling into javascript may modify it
   // out from under us.
-  std::set<ScriptContext*> contexts_copy = contexts_;
-  for (ScriptContext* context : contexts_copy) {
+  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;
@@ -279,26 +94,30 @@
     if (!context_render_view)
       continue;
 
-    if (restrict_to_render_view &&
-        restrict_to_render_view != context_render_view)
+    if (render_view && render_view != context_render_view)
       continue;
 
-    content::RenderFrame* context_render_frame = context->GetRenderFrame();
-    if (!context_render_frame)
-      continue;
+    callback.Run(context);
+  }
+}
 
-    if (run_asynchronously) {
-      blink::WebLocalFrame* web_local_frame =
-          context_render_frame->GetWebFrame();
-      if (!web_local_frame)
-        continue;
-      // WebLocalFrame takes BlinkTaskRunner ownership.
-      web_local_frame->requestRunTask(
-          new BlinkTaskRunner(context_render_frame, context, callback));
-    } else {
-      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