Commit ad6aa8f9 authored by kalman@chromium.org's avatar kalman@chromium.org

Improvements to fatal JavaScript extension errors: (1) include the extension

ID if there is one, (2) only crash extension processes. The latter is a shame -
it'll hide JS problems in the wild - but I'm not comfortable the way it is.

R=jyasskin@chromium.org
TBR=jochen@chromium.org
NOTRY=true

Review URL: https://chromiumcodereview.appspot.com/17104003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@208058 0039d316-1c4b-4281-b951-d872f2087c98
parent 41c8cdf3
...@@ -60,10 +60,13 @@ ChromeV8ContextSet::ContextSet ChromeV8ContextSet::GetAll() const { ...@@ -60,10 +60,13 @@ ChromeV8ContextSet::ContextSet ChromeV8ContextSet::GetAll() const {
} }
ChromeV8Context* ChromeV8ContextSet::GetCurrent() const { ChromeV8Context* ChromeV8ContextSet::GetCurrent() const {
if (!v8::Context::InContext()) return v8::Context::InContext() ?
return NULL; GetByV8Context(v8::Context::GetCurrent()) : NULL;
else }
return GetByV8Context(v8::Context::GetCurrent());
ChromeV8Context* ChromeV8ContextSet::GetCalling() const {
v8::Local<v8::Context> calling = v8::Context::GetCalling();
return calling.IsEmpty() ? NULL : GetByV8Context(calling);
} }
ChromeV8Context* ChromeV8ContextSet::GetByV8Context( ChromeV8Context* ChromeV8ContextSet::GetByV8Context(
......
...@@ -51,10 +51,14 @@ class ChromeV8ContextSet { ...@@ -51,10 +51,14 @@ class ChromeV8ContextSet {
typedef std::set<ChromeV8Context*> ContextSet; typedef std::set<ChromeV8Context*> ContextSet;
ContextSet GetAll() const; ContextSet GetAll() const;
// Gets the ChromeV8Context corresponding to the v8::Context that is // Gets the ChromeV8Context corresponding to v8::Context::GetCurrent(), or
// on the top of the stack, or NULL if no such context exists. // NULL if no such context exists.
ChromeV8Context* GetCurrent() const; ChromeV8Context* GetCurrent() const;
// Gets the ChromeV8Context corresponding to v8::Context::GetCalling(), or
// NULL if no such context exists.
ChromeV8Context* GetCalling() const;
// Gets the ChromeV8Context corresponding to the specified // Gets the ChromeV8Context corresponding to the specified
// v8::Context or NULL if no such context exists. // v8::Context or NULL if no such context exists.
ChromeV8Context* GetByV8Context(v8::Handle<v8::Context> context) const; ChromeV8Context* GetByV8Context(v8::Handle<v8::Context> context) const;
......
...@@ -304,8 +304,12 @@ class ProcessInfoNativeHandler : public ChromeV8Extension { ...@@ -304,8 +304,12 @@ class ProcessInfoNativeHandler : public ChromeV8Extension {
RouteFunction("IsSendRequestDisabled", RouteFunction("IsSendRequestDisabled",
base::Bind(&ProcessInfoNativeHandler::IsSendRequestDisabled, base::Bind(&ProcessInfoNativeHandler::IsSendRequestDisabled,
base::Unretained(this))); base::Unretained(this)));
RouteFunction("HasSwitch",
base::Bind(&ProcessInfoNativeHandler::HasSwitch,
base::Unretained(this)));
} }
private:
void GetExtensionId(const v8::FunctionCallbackInfo<v8::Value>& args) { void GetExtensionId(const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(v8::String::New(extension_id_.c_str())); args.GetReturnValue().Set(v8::String::New(extension_id_.c_str()));
} }
...@@ -330,7 +334,13 @@ class ProcessInfoNativeHandler : public ChromeV8Extension { ...@@ -330,7 +334,13 @@ class ProcessInfoNativeHandler : public ChromeV8Extension {
} }
} }
private: void HasSwitch(const v8::FunctionCallbackInfo<v8::Value>& args) {
CHECK(args.Length() == 1 && args[0]->IsString());
bool has_switch = CommandLine::ForCurrentProcess()->HasSwitch(
*v8::String::AsciiValue(args[0]));
args.GetReturnValue().Set(v8::Boolean::New(has_switch));
}
std::string extension_id_; std::string extension_id_;
std::string context_type_; std::string context_type_;
bool is_incognito_context_; bool is_incognito_context_;
...@@ -971,10 +981,6 @@ void Dispatcher::PopulateSourceMap() { ...@@ -971,10 +981,6 @@ void Dispatcher::PopulateSourceMap() {
IDR_WEB_VIEW_EXPERIMENTAL_JS); IDR_WEB_VIEW_EXPERIMENTAL_JS);
source_map_.RegisterSource("denyWebView", IDR_WEB_VIEW_DENY_JS); source_map_.RegisterSource("denyWebView", IDR_WEB_VIEW_DENY_JS);
source_map_.RegisterSource("adView", IDR_AD_VIEW_JS); source_map_.RegisterSource("adView", IDR_AD_VIEW_JS);
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableAdviewSrcAttribute)) {
source_map_.RegisterSource("adViewCustom", IDR_AD_VIEW_CUSTOM_JS);
}
source_map_.RegisterSource("denyAdView", IDR_AD_VIEW_DENY_JS); source_map_.RegisterSource("denyAdView", IDR_AD_VIEW_DENY_JS);
source_map_.RegisterSource("platformApp", IDR_PLATFORM_APP_JS); source_map_.RegisterSource("platformApp", IDR_PLATFORM_APP_JS);
source_map_.RegisterSource("injectAppTitlebar", IDR_INJECT_APP_TITLEBAR_JS); source_map_.RegisterSource("injectAppTitlebar", IDR_INJECT_APP_TITLEBAR_JS);
...@@ -1114,10 +1120,6 @@ void Dispatcher::DidCreateScriptContext( ...@@ -1114,10 +1120,6 @@ void Dispatcher::DidCreateScriptContext(
is_within_platform_app) { is_within_platform_app) {
if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableAdview)) { if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableAdview)) {
if (extension->HasAPIPermission(APIPermission::kAdView)) { if (extension->HasAPIPermission(APIPermission::kAdView)) {
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableAdviewSrcAttribute)) {
module_system->Require("adViewCustom");
}
module_system->Require("adView"); module_system->Require("adView");
} else { } else {
module_system->Require("denyAdView"); module_system->Require("denyAdView");
......
...@@ -5,10 +5,12 @@ ...@@ -5,10 +5,12 @@
#include "chrome/renderer/extensions/module_system.h" #include "chrome/renderer/extensions/module_system.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h" #include "base/debug/trace_event.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension_messages.h" #include "chrome/common/extensions/extension_messages.h"
#include "chrome/renderer/extensions/chrome_v8_context.h" #include "chrome/renderer/extensions/chrome_v8_context.h"
#include "chrome/renderer/extensions/console.h" #include "chrome/renderer/extensions/console.h"
...@@ -26,9 +28,50 @@ const char* kModuleName = "module_name"; ...@@ -26,9 +28,50 @@ const char* kModuleName = "module_name";
const char* kModuleField = "module_field"; const char* kModuleField = "module_field";
const char* kModulesField = "modules"; const char* kModulesField = "modules";
// Prepends |extension_id| if it's non-empty to |message|.
std::string PrependExtensionID(const std::string& extension_id,
const std::string& message) {
std::string with_extension_id;
if (!extension_id.empty()) {
with_extension_id += "(";
with_extension_id += extension_id;
with_extension_id += ") ";
}
with_extension_id += message;
return with_extension_id;
}
void Fatal(const std::string& extension_id, const std::string& message) {
// Only crash web pages in dev channel.
// Always crash extension processes, or when in single process mode (since
// typically it's used to debug renderer crashes).
bool is_fatal = false;
const CommandLine* command_line = CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kExtensionProcess) ||
command_line->HasSwitch(switches::kSingleProcess)) {
is_fatal = true;
} else {
// <= dev means dev, canary, and trunk.
is_fatal = Feature::GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV;
}
std::string with_extension_id = PrependExtensionID(extension_id, message);
if (is_fatal)
console::Fatal(v8::Context::GetCalling(), with_extension_id);
else
console::Error(v8::Context::GetCalling(), with_extension_id);
}
void Warn(const std::string& extension_id, const std::string& message) {
console::Warn(v8::Context::GetCalling(),
PrependExtensionID(extension_id, message));
}
// Default exception handler which logs the exception. // Default exception handler which logs the exception.
class DefaultExceptionHandler : public ModuleSystem::ExceptionHandler { class DefaultExceptionHandler : public ModuleSystem::ExceptionHandler {
public: public:
explicit DefaultExceptionHandler(const std::string& extension_id)
: extension_id_(extension_id) {}
// Fatally dumps the debug info from |try_catch| to the console. // Fatally dumps the debug info from |try_catch| to the console.
// Make sure this is never used for exceptions that originate in external // Make sure this is never used for exceptions that originate in external
// code! // code!
...@@ -42,9 +85,12 @@ class DefaultExceptionHandler : public ModuleSystem::ExceptionHandler { ...@@ -42,9 +85,12 @@ class DefaultExceptionHandler : public ModuleSystem::ExceptionHandler {
else else
stack_trace = "<could not convert stack trace to string>"; stack_trace = "<could not convert stack trace to string>";
} }
console::Fatal(v8::Context::GetCalling(), Fatal(extension_id_,
CreateExceptionString(try_catch) + "{" + stack_trace + "}"); CreateExceptionString(try_catch) + "{" + stack_trace + "}");
} }
private:
std::string extension_id_;
}; };
} // namespace } // namespace
...@@ -81,7 +127,8 @@ ModuleSystem::ModuleSystem(ChromeV8Context* context, ...@@ -81,7 +127,8 @@ ModuleSystem::ModuleSystem(ChromeV8Context* context,
context_(context), context_(context),
source_map_(source_map), source_map_(source_map),
natives_enabled_(0), natives_enabled_(0),
exception_handler_(new DefaultExceptionHandler()) { exception_handler_(
new DefaultExceptionHandler(context->GetExtensionID())) {
RouteFunction("require", RouteFunction("require",
base::Bind(&ModuleSystem::RequireForJs, base::Unretained(this))); base::Bind(&ModuleSystem::RequireForJs, base::Unretained(this)));
RouteFunction("requireNative", RouteFunction("requireNative",
...@@ -159,7 +206,7 @@ v8::Handle<v8::Value> ModuleSystem::RequireForJsInner( ...@@ -159,7 +206,7 @@ v8::Handle<v8::Value> ModuleSystem::RequireForJsInner(
v8::Handle<v8::Value> modules_value = v8::Handle<v8::Value> modules_value =
global->GetHiddenValue(v8::String::New(kModulesField)); global->GetHiddenValue(v8::String::New(kModulesField));
if (modules_value.IsEmpty() || modules_value->IsUndefined()) { if (modules_value.IsEmpty() || modules_value->IsUndefined()) {
console::Warn(v8::Context::GetCalling(), "Extension view no longer exists"); Warn(context_->GetExtensionID(), "Extension view no longer exists");
return v8::Undefined(); return v8::Undefined();
} }
...@@ -171,8 +218,8 @@ v8::Handle<v8::Value> ModuleSystem::RequireForJsInner( ...@@ -171,8 +218,8 @@ v8::Handle<v8::Value> ModuleSystem::RequireForJsInner(
std::string module_name_str = *v8::String::AsciiValue(module_name); std::string module_name_str = *v8::String::AsciiValue(module_name);
v8::Handle<v8::Value> source(GetSource(module_name_str)); v8::Handle<v8::Value> source(GetSource(module_name_str));
if (source.IsEmpty() || source->IsUndefined()) { if (source.IsEmpty() || source->IsUndefined()) {
console::Error(v8::Context::GetCalling(), Fatal(context_->GetExtensionID(),
"No source for require(" + module_name_str + ")"); "No source for require(" + module_name_str + ")");
return v8::Undefined(); return v8::Undefined();
} }
v8::Handle<v8::String> wrapped_source(WrapSource( v8::Handle<v8::String> wrapped_source(WrapSource(
...@@ -180,8 +227,8 @@ v8::Handle<v8::Value> ModuleSystem::RequireForJsInner( ...@@ -180,8 +227,8 @@ v8::Handle<v8::Value> ModuleSystem::RequireForJsInner(
// Modules are wrapped in (function(){...}) so they always return functions. // Modules are wrapped in (function(){...}) so they always return functions.
v8::Handle<v8::Value> func_as_value = RunString(wrapped_source, module_name); v8::Handle<v8::Value> func_as_value = RunString(wrapped_source, module_name);
if (func_as_value.IsEmpty() || func_as_value->IsUndefined()) { if (func_as_value.IsEmpty() || func_as_value->IsUndefined()) {
console::Error(v8::Context::GetCalling(), Fatal(context_->GetExtensionID(),
"Bad source for require(" + module_name_str + ")"); "Bad source for require(" + module_name_str + ")");
return v8::Undefined(); return v8::Undefined();
} }
...@@ -254,9 +301,8 @@ v8::Local<v8::Value> ModuleSystem::CallModuleMethod( ...@@ -254,9 +301,8 @@ v8::Local<v8::Value> ModuleSystem::CallModuleMethod(
} }
if (module.IsEmpty() || !module->IsObject()) { if (module.IsEmpty() || !module->IsObject()) {
console::Error( Fatal(context_->GetExtensionID(),
v8::Context::GetCalling(), "Failed to get module " + module_name + " to call " + method_name);
"Failed to get module " + module_name + " to call " + method_name);
return handle_scope.Close(v8::Undefined()); return handle_scope.Close(v8::Undefined());
} }
...@@ -264,8 +310,8 @@ v8::Local<v8::Value> ModuleSystem::CallModuleMethod( ...@@ -264,8 +310,8 @@ v8::Local<v8::Value> ModuleSystem::CallModuleMethod(
v8::Handle<v8::Object>::Cast(module)->Get( v8::Handle<v8::Object>::Cast(module)->Get(
v8::String::New(method_name.c_str())); v8::String::New(method_name.c_str()));
if (value.IsEmpty() || !value->IsFunction()) { if (value.IsEmpty() || !value->IsFunction()) {
console::Error(v8::Context::GetCalling(), Fatal(context_->GetExtensionID(),
module_name + "." + method_name + " is not a function"); module_name + "." + method_name + " is not a function");
return handle_scope.Close(v8::Undefined()); return handle_scope.Close(v8::Undefined());
} }
...@@ -329,8 +375,7 @@ void ModuleSystem::LazyFieldGetterInner( ...@@ -329,8 +375,7 @@ void ModuleSystem::LazyFieldGetterInner(
if (module_system_value.IsEmpty() || !module_system_value->IsExternal()) { if (module_system_value.IsEmpty() || !module_system_value->IsExternal()) {
// ModuleSystem has been deleted. // ModuleSystem has been deleted.
// TODO(kalman): See comment in header file. // TODO(kalman): See comment in header file.
console::Warn(v8::Context::GetCalling(), Warn("", "Module system has been deleted, does extension view exist?");
"Module system has been deleted, does extension view exist?");
return; return;
} }
...@@ -362,9 +407,9 @@ void ModuleSystem::LazyFieldGetterInner( ...@@ -362,9 +407,9 @@ void ModuleSystem::LazyFieldGetterInner(
if (!module->Has(field)) { if (!module->Has(field)) {
std::string field_str = *v8::String::AsciiValue(field); std::string field_str = *v8::String::AsciiValue(field);
console::Fatal(v8::Context::GetCalling(), Fatal(module_system->context_->GetExtensionID(),
"Lazy require of " + name + "." + field_str + " did not " + "Lazy require of " + name + "." + field_str + " did not " +
"set the " + field_str + " field"); "set the " + field_str + " field");
return; return;
} }
...@@ -464,8 +509,8 @@ v8::Handle<v8::Value> ModuleSystem::RequireNativeFromString( ...@@ -464,8 +509,8 @@ v8::Handle<v8::Value> ModuleSystem::RequireNativeFromString(
// we could crash. // we could crash.
if (exception_handler_) if (exception_handler_)
return v8::ThrowException(v8::String::New("Natives disabled")); return v8::ThrowException(v8::String::New("Natives disabled"));
console::Fatal(v8::Context::GetCalling(), Fatal(context_->GetExtensionID(),
"Natives disabled for requireNative(" + native_name + ")"); "Natives disabled for requireNative(" + native_name + ")");
return v8::Undefined(); return v8::Undefined();
} }
...@@ -474,9 +519,8 @@ v8::Handle<v8::Value> ModuleSystem::RequireNativeFromString( ...@@ -474,9 +519,8 @@ v8::Handle<v8::Value> ModuleSystem::RequireNativeFromString(
NativeHandlerMap::iterator i = native_handler_map_.find(native_name); NativeHandlerMap::iterator i = native_handler_map_.find(native_name);
if (i == native_handler_map_.end()) { if (i == native_handler_map_.end()) {
console::Fatal( Fatal(context_->GetExtensionID(),
v8::Context::GetCalling(), "Couldn't find native for requireNative(" + native_name + ")");
"Couldn't find native for requireNative(" + native_name + ")");
return v8::Undefined(); return v8::Undefined();
} }
return i->second->NewInstance(); return i->second->NewInstance();
......
...@@ -10,17 +10,16 @@ ...@@ -10,17 +10,16 @@
// TODO(rpaquay): This file is currently very similar to "web_view.js". Do we // TODO(rpaquay): This file is currently very similar to "web_view.js". Do we
// want to refactor to extract common pieces? // want to refactor to extract common pieces?
var adViewCustom = require('adViewCustom'); var process = requireNative('process');
var watchForTag = require('tagWatcher').watchForTag; var watchForTag = require('tagWatcher').watchForTag;
/** /**
* Define "allowCustomAdNetworks" function such that it returns "true" if the * Define "allowCustomAdNetworks" function such that the
* "adViewCustom" module was injected. This is so that the
* "kEnableAdviewSrcAttribute" flag is respected. * "kEnableAdviewSrcAttribute" flag is respected.
*/ */
var allowCustomAdNetworks = (function(allow){ function allowCustomAdNetworks() {
return function() { return Boolean(allow); } return process.HasSwitch('enable-adview-src-attribute');
})(adViewCustom ? adViewCustom.enabled : false); }
/** /**
* List of attribute names to "blindly" sync between <adview> tag and internal * List of attribute names to "blindly" sync between <adview> tag and internal
......
// Copyright (c) 2013 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.
exports.enabled = true;
...@@ -47,7 +47,6 @@ without changes to the corresponding grd file. fb9 --> ...@@ -47,7 +47,6 @@ without changes to the corresponding grd file. fb9 -->
<!-- Custom bindings for extension APIs. --> <!-- Custom bindings for extension APIs. -->
<include name="IDR_AD_VIEW_DENY_JS" file="extensions\ad_view_deny.js" type="BINDATA" /> <include name="IDR_AD_VIEW_DENY_JS" file="extensions\ad_view_deny.js" type="BINDATA" />
<include name="IDR_AD_VIEW_JS" file="extensions\ad_view.js" type="BINDATA" /> <include name="IDR_AD_VIEW_JS" file="extensions\ad_view.js" type="BINDATA" />
<include name="IDR_AD_VIEW_CUSTOM_JS" file="extensions\ad_view_custom.js" type="BINDATA" />
<include name="IDR_APP_CUSTOM_BINDINGS_JS" file="extensions\app_custom_bindings.js" type="BINDATA" /> <include name="IDR_APP_CUSTOM_BINDINGS_JS" file="extensions\app_custom_bindings.js" type="BINDATA" />
<include name="IDR_APP_RUNTIME_CUSTOM_BINDINGS_JS" file="extensions\app_runtime_custom_bindings.js" type="BINDATA" /> <include name="IDR_APP_RUNTIME_CUSTOM_BINDINGS_JS" file="extensions\app_runtime_custom_bindings.js" type="BINDATA" />
<include name="IDR_APP_WINDOW_CUSTOM_BINDINGS_JS" file="extensions\app_window_custom_bindings.js" type="BINDATA" /> <include name="IDR_APP_WINDOW_CUSTOM_BINDINGS_JS" file="extensions\app_window_custom_bindings.js" type="BINDATA" />
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment