0

Add a console interface for logging to the JS console from a PPAPI plugin.

TEST=manual
Review URL: http://codereview.chromium.org/6667010

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@77852 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
brettw@chromium.org
2011-03-11 20:20:56 +00:00
parent c8d7021f04
commit d38c5740cd
16 changed files with 385 additions and 22 deletions

@ -0,0 +1,45 @@
/* Copyright (c) 2011 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.
*/
#ifndef PPAPI_C_DEV_PPB_CONSOLE_DEV_H_
#define PPAPI_C_DEV_PPB_CONSOLE_DEV_H_
#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_var.h"
#define PPB_CONSOLE_DEV_INTERFACE "PPB_Console(Dev);0.1"
typedef enum {
PP_LOGLEVEL_TIP = 0,
PP_LOGLEVEL_LOG,
PP_LOGLEVEL_WARNING,
PP_LOGLEVEL_ERROR
} PP_LogLevel_Dev;
struct PPB_Console_Dev {
/**
* Logs the given message to the JavaScript console associated with the
* given plugin instance with the given logging level. The name of the plugin
* issuing the log message will be automatically prepended to the message.
* The value may be any type of Var.
*/
void (*Log)(PP_Instance instance, PP_LogLevel_Dev level, struct PP_Var value);
/**
* Logs a message to the console with the given source information rather
* than using the internal PPAPI plugin name. The name must be a string var.
*
* The regular log function will automatically prepend the name of your
* plugin to the message as the "source" of the message. Some plugins may
* wish to override this. For example, if your plugin is a Python
* interpreter, you would want log messages to contain the source .py file
* doing the log statement rather than have "python" show up in the console.
*/
void (*LogWithSource)(PP_Instance instance,
PP_LogLevel_Dev level,
struct PP_Var source,
struct PP_Var value);
};
#endif // PPAPI_C_DEV_PPB_CONSOLE_DEV_H_

@ -11,6 +11,7 @@
#include <algorithm>
#include "ppapi/c/dev/ppb_console_dev.h"
#include "ppapi/c/dev/ppp_printing_dev.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/pp_input_event.h"
@ -175,6 +176,14 @@ class MyInstance : public pp::Instance, public MyFetcherClient {
return true;
}
void Log(PP_LogLevel_Dev level, const pp::Var& value) {
const PPB_Console_Dev* console = reinterpret_cast<const PPB_Console_Dev*>(
pp::Module::Get()->GetBrowserInterface(PPB_CONSOLE_DEV_INTERFACE));
if (!console)
return;
console->Log(pp_instance(), level, value.pp_var());
}
virtual bool HandleDocumentLoad(const pp::URLLoader& loader) {
fetcher_ = new MyFetcher();
fetcher_->StartWithOpenedLoader(loader, this);
@ -184,7 +193,7 @@ class MyInstance : public pp::Instance, public MyFetcherClient {
virtual bool HandleInputEvent(const PP_InputEvent& event) {
switch (event.type) {
case PP_INPUTEVENT_TYPE_MOUSEDOWN:
//SayHello();
SayHello();
return true;
case PP_INPUTEVENT_TYPE_MOUSEMOVE:
return true;
@ -242,6 +251,7 @@ class MyInstance : public pp::Instance, public MyFetcherClient {
}
virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
Log(PP_LOGLEVEL_LOG, "DidChangeView");
if (position.size().width() == width_ &&
position.size().height() == height_)
return; // We don't care about the position, only the size.
@ -342,18 +352,6 @@ class MyInstance : public pp::Instance, public MyFetcherClient {
}
private:
void Log(const pp::Var& var) {
pp::Var doc = GetWindowObject().GetProperty("document");
if (console_.is_undefined()) {
pp::Var body = doc.GetProperty("body");
console_ = doc.Call("createElement", "pre");
console_.GetProperty("style").SetProperty("backgroundColor", "lightgray");
body.Call("appendChild", console_);
}
console_.Call("appendChild", doc.Call("createTextNode", var));
console_.Call("appendChild", doc.Call("createTextNode", "\n"));
}
void SayHello() {
pp::Var window = GetWindowObject();
pp::Var doc = window.GetProperty("document");
@ -362,20 +360,20 @@ class MyInstance : public pp::Instance, public MyFetcherClient {
pp::Var obj(this, new MyScriptableObject(this));
// Our object should have its toString method called.
Log("Testing MyScriptableObject::toString():");
Log(obj);
Log(PP_LOGLEVEL_LOG, "Testing MyScriptableObject::toString():");
Log(PP_LOGLEVEL_LOG, obj);
// body.appendChild(body) should throw an exception
Log("\nCalling body.appendChild(body):");
Log(PP_LOGLEVEL_LOG, "Calling body.appendChild(body):");
pp::Var exception;
body.Call("appendChild", body, &exception);
Log(exception);
Log(PP_LOGLEVEL_LOG, exception);
Log("\nEnumeration of window properties:");
Log(PP_LOGLEVEL_LOG, "Enumeration of window properties:");
std::vector<pp::Var> props;
window.GetAllPropertyNames(&props);
for (size_t i = 0; i < props.size(); ++i)
Log(props[i]);
Log(PP_LOGLEVEL_LOG, props[i]);
pp::Var location = window.GetProperty("location");
pp::Var href = location.GetProperty("href");
@ -387,11 +385,11 @@ class MyInstance : public pp::Instance, public MyFetcherClient {
}
void DidFetch(bool success, const std::string& data) {
Log("\nDownloaded location.href:");
Log(PP_LOGLEVEL_LOG, "Downloaded location.href:");
if (success) {
Log(data);
Log(PP_LOGLEVEL_LOG, data);
} else {
Log("Failed to download.");
Log(PP_LOGLEVEL_ERROR, "Failed to download.");
}
delete fetcher_;
fetcher_ = NULL;

@ -51,6 +51,7 @@
'c/dev/ppb_char_set_dev.h',
'c/dev/ppb_context_3d_dev.h',
'c/dev/ppb_context_3d_trusted_dev.h',
'c/dev/ppb_console_dev.h',
'c/dev/ppb_cursor_control_dev.h',
'c/dev/ppb_directory_reader_dev.h',
'c/dev/ppb_file_chooser_dev.h',

@ -81,6 +81,8 @@
'proxy/ppb_buffer_proxy.h',
'proxy/ppb_char_set_proxy.cc',
'proxy/ppb_char_set_proxy.h',
'proxy/ppb_console_proxy.cc',
'proxy/ppb_console_proxy.h',
'proxy/ppb_context_3d_proxy.cc',
'proxy/ppb_context_3d_proxy.h',
'proxy/ppb_core_proxy.cc',

@ -47,6 +47,7 @@
#include "ppapi/proxy/ppb_audio_proxy.h"
#include "ppapi/proxy/ppb_buffer_proxy.h"
#include "ppapi/proxy/ppb_char_set_proxy.h"
#include "ppapi/proxy/ppb_console_proxy.h"
#include "ppapi/proxy/ppb_context_3d_proxy.h"
#include "ppapi/proxy/ppb_core_proxy.h"
#include "ppapi/proxy/ppb_cursor_control_proxy.h"
@ -105,6 +106,7 @@ InterfaceList::InterfaceList() {
AddPPB(PPB_Audio_Proxy::GetInfo());
AddPPB(PPB_Buffer_Proxy::GetInfo());
AddPPB(PPB_CharSet_Proxy::GetInfo());
AddPPB(PPB_Console_Proxy::GetInfo());
AddPPB(PPB_Context3D_Proxy::GetInfo());
AddPPB(PPB_Core_Proxy::GetInfo());
AddPPB(PPB_CursorControl_Proxy::GetInfo());

@ -17,6 +17,7 @@ enum InterfaceID {
INTERFACE_ID_PPB_AUDIO_CONFIG,
INTERFACE_ID_PPB_BUFFER,
INTERFACE_ID_PPB_CHAR_SET,
INTERFACE_ID_PPB_CONSOLE,
INTERFACE_ID_PPB_CONTEXT_3D,
INTERFACE_ID_PPB_CORE,
INTERFACE_ID_PPB_CURSORCONTROL,

@ -216,6 +216,17 @@ IPC_SYNC_MESSAGE_ROUTED2_2(PpapiHostMsg_PPBBuffer_Create,
pp::proxy::HostResource /* result_resource */,
int32_t /* result_shm_handle */)
// PPB_Console.
IPC_MESSAGE_ROUTED3(PpapiHostMsg_PPBConsole_Log,
PP_Instance /* instance */,
int /* log_level */,
pp::proxy::SerializedVar /* value */)
IPC_MESSAGE_ROUTED4(PpapiHostMsg_PPBConsole_LogWithSource,
PP_Instance /* instance */,
int /* log_level */,
pp::proxy::SerializedVar /* soruce */,
pp::proxy::SerializedVar /* value */)
// PPB_Context3D.
IPC_SYNC_MESSAGE_ROUTED3_1(PpapiHostMsg_PPBContext3D_Create,
PP_Instance /* instance */,

@ -0,0 +1,103 @@
// Copyright (c) 2011 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 "ppapi/proxy/ppb_console_proxy.h"
#include "ppapi/c/dev/ppb_console_dev.h"
#include "ppapi/proxy/plugin_dispatcher.h"
#include "ppapi/proxy/ppapi_messages.h"
namespace pp {
namespace proxy {
namespace {
void Log(PP_Instance instance, PP_LogLevel_Dev level, PP_Var value) {
PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
if (!dispatcher)
return;
dispatcher->Send(new PpapiHostMsg_PPBConsole_Log(
INTERFACE_ID_PPB_CONSOLE, instance, static_cast<int>(level),
SerializedVarSendInput(dispatcher, value)));
}
void LogWithSource(PP_Instance instance,
PP_LogLevel_Dev level,
const PP_Var source,
const PP_Var value) {
PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
if (!dispatcher)
return;
dispatcher->Send(new PpapiHostMsg_PPBConsole_LogWithSource(
INTERFACE_ID_PPB_CONSOLE, instance, static_cast<int>(level),
SerializedVarSendInput(dispatcher, source),
SerializedVarSendInput(dispatcher, value)));
}
const PPB_Console_Dev console_interface = {
&Log,
&LogWithSource
};
InterfaceProxy* CreateConsoleProxy(Dispatcher* dispatcher,
const void* target_interface) {
return new PPB_Console_Proxy(dispatcher, target_interface);
}
} // namespace
PPB_Console_Proxy::PPB_Console_Proxy(Dispatcher* dispatcher,
const void* target_interface)
: InterfaceProxy(dispatcher, target_interface) {
}
PPB_Console_Proxy::~PPB_Console_Proxy() {
}
// static
const InterfaceProxy::Info* PPB_Console_Proxy::GetInfo() {
static const Info info = {
&console_interface,
PPB_CONSOLE_DEV_INTERFACE,
INTERFACE_ID_PPB_CONSOLE,
false,
&CreateConsoleProxy,
};
return &info;
}
bool PPB_Console_Proxy::OnMessageReceived(const IPC::Message& msg) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(PPB_Console_Proxy, msg)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBConsole_Log,
OnMsgLog)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBConsole_LogWithSource,
OnMsgLogWithSource)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void PPB_Console_Proxy::OnMsgLog(PP_Instance instance,
int log_level,
SerializedVarReceiveInput value) {
ppb_console_target()->Log(instance,
static_cast<PP_LogLevel_Dev>(log_level),
value.Get(dispatcher()));
}
void PPB_Console_Proxy::OnMsgLogWithSource(PP_Instance instance,
int log_level,
SerializedVarReceiveInput source,
SerializedVarReceiveInput value) {
ppb_console_target()->LogWithSource(
instance,
static_cast<PP_LogLevel_Dev>(log_level),
source.Get(dispatcher()),
value.Get(dispatcher()));
}
} // namespace proxy
} // namespace pp

@ -0,0 +1,48 @@
// Copyright (c) 2011 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.
#ifndef PPAPI_PROXY_PPB_CONSOLE_PROXY_H_
#define PPAPI_PROXY_PPB_CONSOLE_PROXY_H_
#include "base/basictypes.h"
#include "ppapi/c/pp_instance.h"
#include "ppapi/proxy/interface_proxy.h"
#include "ppapi/proxy/serialized_var.h"
struct PPB_Console_Dev;
namespace pp {
namespace proxy {
class PPB_Console_Proxy : public InterfaceProxy {
public:
PPB_Console_Proxy(Dispatcher* dispatcher, const void* target_interface);
virtual ~PPB_Console_Proxy();
static const Info* GetInfo();
const PPB_Console_Dev* ppb_console_target() const {
return static_cast<const PPB_Console_Dev*>(target_interface());
}
// InterfaceProxy implementation.
virtual bool OnMessageReceived(const IPC::Message& msg);
private:
// Message handlers.
void OnMsgLog(PP_Instance instance,
int log_level,
SerializedVarReceiveInput value);
void OnMsgLogWithSource(PP_Instance instance,
int log_level,
SerializedVarReceiveInput source,
SerializedVarReceiveInput value);
DISALLOW_COPY_AND_ASSIGN(PPB_Console_Proxy);
};
} // namespace proxy
} // namespace pp
#endif // PPAPI_PROXY_PPB_CONSOLE_PROXY_H_

@ -15,6 +15,7 @@
#include "ppapi/c/dev/pp_video_dev.h"
#include "ppapi/c/dev/ppb_buffer_dev.h"
#include "ppapi/c/dev/ppb_char_set_dev.h"
#include "ppapi/c/dev/ppb_console_dev.h"
#include "ppapi/c/dev/ppb_context_3d_dev.h"
#include "ppapi/c/dev/ppb_context_3d_trusted_dev.h"
#include "ppapi/c/dev/ppb_cursor_control_dev.h"

@ -283,6 +283,8 @@
'../plugins/ppapi/ppb_buffer_impl.h',
'../plugins/ppapi/ppb_char_set_impl.cc',
'../plugins/ppapi/ppb_char_set_impl.h',
'../plugins/ppapi/ppb_console_impl.cc',
'../plugins/ppapi/ppb_console_impl.h',
'../plugins/ppapi/ppb_context_3d_impl.cc',
'../plugins/ppapi/ppb_context_3d_impl.h',
'../plugins/ppapi/ppb_cursor_control_impl.cc',

@ -16,6 +16,7 @@
#include "ppapi/c/dev/ppb_char_set_dev.h"
#include "ppapi/c/dev/ppb_context_3d_dev.h"
#include "ppapi/c/dev/ppb_context_3d_trusted_dev.h"
#include "ppapi/c/dev/ppb_console_dev.h"
#include "ppapi/c/dev/ppb_cursor_control_dev.h"
#include "ppapi/c/dev/ppb_directory_reader_dev.h"
#include "ppapi/c/dev/ppb_file_io_dev.h"
@ -65,6 +66,7 @@
#include "webkit/plugins/ppapi/ppb_audio_impl.h"
#include "webkit/plugins/ppapi/ppb_buffer_impl.h"
#include "webkit/plugins/ppapi/ppb_char_set_impl.h"
#include "webkit/plugins/ppapi/ppb_console_impl.h"
#include "webkit/plugins/ppapi/ppb_cursor_control_impl.h"
#include "webkit/plugins/ppapi/ppb_directory_reader_impl.h"
#include "webkit/plugins/ppapi/ppb_file_chooser_impl.h"
@ -230,6 +232,8 @@ const void* GetInterface(const char* name) {
return PPB_CharSet_Impl::GetInterface();
if (strcmp(name, PPB_CLASS_INTERFACE) == 0)
return VarObjectClass::GetInterface();
if (strcmp(name, PPB_CONSOLE_DEV_INTERFACE) == 0)
return PPB_Console_Impl::GetInterface();
if (strcmp(name, PPB_CORE_INTERFACE) == 0)
return &core_interface;
if (strcmp(name, PPB_CURSOR_CONTROL_DEV_INTERFACE) == 0)

@ -0,0 +1,86 @@
// Copyright (c) 2011 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 "webkit/plugins/ppapi/ppb_console_impl.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "ppapi/c/dev/ppb_console_dev.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebConsoleMessage.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h"
#include "webkit/plugins/ppapi/plugin_module.h"
#include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
#include "webkit/plugins/ppapi/resource_tracker.h"
#include "webkit/plugins/ppapi/var.h"
using WebKit::WebConsoleMessage;
using WebKit::WebString;
namespace webkit {
namespace ppapi {
namespace {
void LogWithSource(PP_Instance instance_id,
PP_LogLevel_Dev level,
PP_Var source,
PP_Var value) {
PluginInstance* instance = ResourceTracker::Get()->GetInstance(instance_id);
if (!instance)
return;
// Convert the log level, defaulting to error.
WebConsoleMessage::Level web_level;
switch (level) {
case PP_LOGLEVEL_TIP:
web_level = WebConsoleMessage::LevelTip;
break;
case PP_LOGLEVEL_LOG:
web_level = WebConsoleMessage::LevelLog;
break;
case PP_LOGLEVEL_WARNING:
web_level = WebConsoleMessage::LevelWarning;
break;
case PP_LOGLEVEL_ERROR:
default:
web_level = WebConsoleMessage::LevelError;
break;
}
// Format is the "<source>: <value>". The source defaults to the module name
// if the source isn't a string or is empty.
std::string message;
if (source.type == PP_VARTYPE_STRING)
message = Var::PPVarToLogString(source);
if (message.empty())
message = instance->module()->name();
message.append(": ");
message.append(Var::PPVarToLogString(value));
instance->container()->element().document().frame()->addMessageToConsole(
WebConsoleMessage(web_level, WebString(UTF8ToUTF16(message))));
}
void Log(PP_Instance instance, PP_LogLevel_Dev level, PP_Var value) {
LogWithSource(instance, level, PP_MakeUndefined(), value);
}
const PPB_Console_Dev ppb_console = {
&Log,
&LogWithSource
};
} // namespace
// static
const struct PPB_Console_Dev* PPB_Console_Impl::GetInterface() {
return &ppb_console;
}
} // namespace ppapi
} // namespace webkit

@ -0,0 +1,23 @@
// Copyright (c) 2011 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.
#ifndef WEBKIT_PLUGINS_PPAPI_PPB_CONSOLE_IMPL_H_
#define WEBKIT_PLUGINS_PPAPI_PPB_CONSOLE_IMPL_H_
struct PPB_Console_Dev;
namespace webkit {
namespace ppapi {
class PPB_Console_Impl {
public:
// Returns a pointer to the interface implementing PPB_Console_Dev that is
// exposed to the plugin.
static const PPB_Console_Dev* GetInterface();
};
} // namespace ppapi
} // namespace webkit
#endif // WEBKIT_PLUGINS_PPAPI_PPB_CONSOLE_IMPL_H_

@ -8,6 +8,7 @@
#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "ppapi/c/dev/ppb_var_deprecated.h"
#include "ppapi/c/ppb_var.h"
@ -723,6 +724,38 @@ PP_Var Var::NPIdentifierToPPVar(PluginModule* module, NPIdentifier id) {
return PP_MakeInt32(int_value);
}
// static
std::string Var::PPVarToLogString(PP_Var var) {
switch (var.type) {
case PP_VARTYPE_UNDEFINED:
return "[Undefined]";
case PP_VARTYPE_NULL:
return "[Null]";
case PP_VARTYPE_BOOL:
return var.value.as_bool ? "[True]" : "[False]";
case PP_VARTYPE_INT32:
return base::IntToString(var.value.as_int);
case PP_VARTYPE_DOUBLE:
return base::DoubleToString(var.value.as_double);
case PP_VARTYPE_STRING: {
scoped_refptr<StringVar> string(StringVar::FromPPVar(var));
if (!string)
return "[Invalid string]";
// Since this is for logging, escape NULLs.
std::string result = string->value();
std::string null;
null.push_back(0);
ReplaceSubstringsAfterOffset(&result, 0, null, "\\0");
return result;
}
case PP_VARTYPE_OBJECT:
return "[Object]";
default:
return "[Invalid var]";
}
}
// static
void Var::PluginAddRefPPVar(PP_Var var) {
if (var.type == PP_VARTYPE_STRING || var.type == PP_VARTYPE_OBJECT) {

@ -54,6 +54,9 @@ class Var : public base::RefCounted<Var> {
// given module. A returned string will have a reference count of 1.
static PP_Var NPIdentifierToPPVar(PluginModule* module, NPIdentifier id);
// Returns a string representing the given var for logging purposes.
static std::string PPVarToLogString(PP_Var var);
// Provides access to the manual refcounting of a PP_Var from the plugin's
// perspective. This is different than the AddRef/Release on this scoped
// object. This uses the ResourceTracker, which keeps a separate "plugin