diff options
author | kalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-21 02:56:02 +0000 |
---|---|---|
committer | kalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-21 02:56:02 +0000 |
commit | 5322973d577a1064bb32eb19bd01c6aa5e7c51a5 (patch) | |
tree | 43f6b82ae3b489cbf90473b180200e0120791b8c | |
parent | 768c0ff262cb7af622df394e9f2c1dbfe53e0162 (diff) | |
download | chromium_src-5322973d577a1064bb32eb19bd01c6aa5e7c51a5.zip chromium_src-5322973d577a1064bb32eb19bd01c6aa5e7c51a5.tar.gz chromium_src-5322973d577a1064bb32eb19bd01c6aa5e7c51a5.tar.bz2 |
Go back to JSON serialization of extension messages. base::Value pickling was
causing OOMs on the browser, and V8ValueConverter has bugs. This is effectively
a revert of both r204067 and r204496, except the json library replaced by the new
safe $JSON, and it's eagerly included via our small-footprint v8::Extension.
BUG=247530,248019,249419
R=mpcomplete@chromium.org
TBR=cdn@chromium.org
Review URL: https://chromiumcodereview.appspot.com/17144003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@207675 0039d316-1c4b-4281-b951-d872f2087c98
25 files changed, 208 insertions, 163 deletions
diff --git a/chrome/browser/extensions/api/messaging/extension_message_port.cc b/chrome/browser/extensions/api/messaging/extension_message_port.cc index a3bba97..e4782c7 100644 --- a/chrome/browser/extensions/api/messaging/extension_message_port.cc +++ b/chrome/browser/extensions/api/messaging/extension_message_port.cc @@ -4,7 +4,6 @@ #include "chrome/browser/extensions/api/messaging/extension_message_port.h" -#include "base/values.h" #include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/extensions/extension_system.h" @@ -46,11 +45,10 @@ void ExtensionMessagePort::DispatchOnDisconnect( routing_id_, source_port_id, error_message)); } -void ExtensionMessagePort::DispatchOnMessage( - scoped_ptr<base::ListValue> message, - int target_port_id) { - process_->Send(new ExtensionMsg_DeliverMessage( - routing_id_, target_port_id, *message)); +void ExtensionMessagePort::DispatchOnMessage(const std::string& message, + int target_port_id) { + process_->Send(new ExtensionMsg_DeliverMessage( + routing_id_, target_port_id, message)); } void ExtensionMessagePort::IncrementLazyKeepaliveCount() { diff --git a/chrome/browser/extensions/api/messaging/extension_message_port.h b/chrome/browser/extensions/api/messaging/extension_message_port.h index 7cc386a..ccf9b69 100644 --- a/chrome/browser/extensions/api/messaging/extension_message_port.h +++ b/chrome/browser/extensions/api/messaging/extension_message_port.h @@ -9,10 +9,6 @@ class GURL; -namespace base { -class ListValue; -} - namespace content { class RenderProcessHost; } // namespace content @@ -34,7 +30,7 @@ class ExtensionMessagePort : public MessageService::MessagePort { const GURL& source_url) OVERRIDE; virtual void DispatchOnDisconnect(int source_port_id, const std::string& error_message) OVERRIDE; - virtual void DispatchOnMessage(scoped_ptr<base::ListValue> message, + virtual void DispatchOnMessage(const std::string& message, int target_port_id) OVERRIDE; virtual void IncrementLazyKeepaliveCount() OVERRIDE; virtual void DecrementLazyKeepaliveCount() OVERRIDE; diff --git a/chrome/browser/extensions/api/messaging/message_service.cc b/chrome/browser/extensions/api/messaging/message_service.cc index 3188a7f..3d05766 100644 --- a/chrome/browser/extensions/api/messaging/message_service.cc +++ b/chrome/browser/extensions/api/messaging/message_service.cc @@ -6,7 +6,6 @@ #include "base/atomic_sequence_num.h" #include "base/bind.h" -#include "base/bind_helpers.h" #include "base/callback.h" #include "base/json/json_writer.h" #include "base/lazy_instance.h" @@ -458,7 +457,7 @@ void MessageService::CloseChannelImpl( } void MessageService::PostMessage( - int source_port_id, scoped_ptr<base::ListValue> message) { + int source_port_id, const std::string& message) { int channel_id = GET_CHANNEL_ID(source_port_id); MessageChannelMap::iterator iter = channels_.find(channel_id); if (iter == channels_.end()) { @@ -469,9 +468,7 @@ void MessageService::PostMessage( lazy_background_task_queue_->AddPendingTask( pending->second.first, pending->second.second, base::Bind(&MessageService::PendingPostMessage, - weak_factory_.GetWeakPtr(), - source_port_id, - base::Passed(&message))); + weak_factory_.GetWeakPtr(), source_port_id, message)); } return; } @@ -481,13 +478,12 @@ void MessageService::PostMessage( MessagePort* port = IS_OPENER_PORT_ID(dest_port_id) ? iter->second->opener.get() : iter->second->receiver.get(); - port->DispatchOnMessage(message.Pass(), dest_port_id); + port->DispatchOnMessage(message, dest_port_id); } -void MessageService::PostMessageFromNativeProcess( - int port_id, - scoped_ptr<base::ListValue> message) { - PostMessage(port_id, message.Pass()); +void MessageService::PostMessageFromNativeProcess(int port_id, + const std::string& message) { + PostMessage(port_id, message); } void MessageService::Observe(int type, diff --git a/chrome/browser/extensions/api/messaging/message_service.h b/chrome/browser/extensions/api/messaging/message_service.h index 59ba6ff..6de5a74 100644 --- a/chrome/browser/extensions/api/messaging/message_service.h +++ b/chrome/browser/extensions/api/messaging/message_service.h @@ -22,7 +22,6 @@ class Profile; namespace base { class DictionaryValue; -class ListValue; } namespace content { @@ -83,7 +82,7 @@ class MessageService : public ProfileKeyedAPI, const std::string& error_message) {} // Dispatch a message to this end of the communication. - virtual void DispatchOnMessage(scoped_ptr<base::ListValue> message, + virtual void DispatchOnMessage(const std::string& message, int target_port_id) = 0; // MessagPorts that target extensions will need to adjust their keepalive @@ -145,12 +144,12 @@ class MessageService : public ProfileKeyedAPI, const std::string& error_message) OVERRIDE; // Sends a message to the given port. - void PostMessage(int port_id, scoped_ptr<base::ListValue> message); + void PostMessage(int port_id, const std::string& message); // NativeMessageProcessHost::Client virtual void PostMessageFromNativeProcess( int port_id, - scoped_ptr<base::ListValue> message) OVERRIDE; + const std::string& message) OVERRIDE; private: friend class MockMessageService; @@ -206,10 +205,10 @@ class MessageService : public ProfileKeyedAPI, CloseChannel(port_id, error_message); } void PendingPostMessage(int port_id, - scoped_ptr<base::ListValue> message, + const std::string& message, extensions::ExtensionHost* host) { if (host) - PostMessage(port_id, message.Pass()); + PostMessage(port_id, message); } // Immediate dispatches a disconnect to |source| for |port_id|. Sets source's diff --git a/chrome/browser/extensions/api/messaging/native_message_port.cc b/chrome/browser/extensions/api/messaging/native_message_port.cc index 7ca7bea..e746e29 100644 --- a/chrome/browser/extensions/api/messaging/native_message_port.cc +++ b/chrome/browser/extensions/api/messaging/native_message_port.cc @@ -5,8 +5,6 @@ #include "chrome/browser/extensions/api/messaging/native_message_port.h" #include "base/bind.h" -#include "base/json/json_writer.h" -#include "base/values.h" #include "chrome/browser/extensions/api/messaging/native_message_process_host.h" #include "content/public/browser/browser_thread.h" @@ -21,20 +19,12 @@ NativeMessagePort::~NativeMessagePort() { content::BrowserThread::IO, FROM_HERE, native_process_); } -void NativeMessagePort::DispatchOnMessage(scoped_ptr<base::ListValue> message, +void NativeMessagePort::DispatchOnMessage(const std::string& message, int target_port_id) { - std::string message_as_json; - if (!message->empty()) { - DCHECK_EQ(1u, message->GetSize()); - base::Value* value = NULL; - message->Get(0, &value); - base::JSONWriter::Write(value, &message_as_json); - } content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, base::Bind(&NativeMessageProcessHost::Send, - base::Unretained(native_process_), - message_as_json)); + base::Unretained(native_process_), message)); } } // namespace extensions diff --git a/chrome/browser/extensions/api/messaging/native_message_port.h b/chrome/browser/extensions/api/messaging/native_message_port.h index 4c88445..6afce8a 100644 --- a/chrome/browser/extensions/api/messaging/native_message_port.h +++ b/chrome/browser/extensions/api/messaging/native_message_port.h @@ -16,7 +16,7 @@ class NativeMessagePort : public MessageService::MessagePort { // Takes ownership of |native_process|. explicit NativeMessagePort(NativeMessageProcessHost* native_process); virtual ~NativeMessagePort(); - virtual void DispatchOnMessage(scoped_ptr<base::ListValue> message, + virtual void DispatchOnMessage(const std::string& message, int target_port_id) OVERRIDE; private: diff --git a/chrome/browser/extensions/api/messaging/native_message_process_host.cc b/chrome/browser/extensions/api/messaging/native_message_process_host.cc index 9f8c50b..9187a08 100644 --- a/chrome/browser/extensions/api/messaging/native_message_process_host.cc +++ b/chrome/browser/extensions/api/messaging/native_message_process_host.cc @@ -5,9 +5,7 @@ #include "chrome/browser/extensions/api/messaging/native_message_process_host.h" #include "base/bind.h" -#include "base/bind_helpers.h" #include "base/files/file_path.h" -#include "base/json/json_reader.h" #include "base/logging.h" #include "base/platform_file.h" #include "base/process_util.h" @@ -44,8 +42,6 @@ const char kForbiddenError[] = "Access to the specified native messaging host is forbidden."; const char kHostInputOuputError[] = "Error when communicating with the native messaging host."; -const char kInvalidJsonError[] = - "Message must be valid JSON"; } // namespace @@ -272,35 +268,10 @@ void NativeMessageProcessHost::ProcessIncomingData( if (incoming_data_.size() < message_size + kMessageHeaderSize) return; - scoped_ptr<base::ListValue> message(new base::ListValue()); - { - std::string message_as_json = - incoming_data_.substr(kMessageHeaderSize, message_size); - int error_code; - std::string error_message; - scoped_ptr<base::Value> message_as_value( - base::JSONReader::ReadAndReturnError(message_as_json, - 0, // no flags - &error_code, - &error_message)); - if (!message_as_value) { - base::JSONReader::JsonParseError parse_error = - static_cast<base::JSONReader::JsonParseError>(error_code); - LOG(ERROR) << "Native Messaging host sent message with invalid JSON \"" - << message_as_json << "\": " << error_message << " (" - << base::JSONReader::ErrorCodeToString(parse_error) << "), " - << "message size " << message_size << ", " - << "incoming data size " << incoming_data_.size() << "."; - Close(kInvalidJsonError); - return; - } - message->Append(message_as_value.release()); - } - content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, base::Bind(&Client::PostMessageFromNativeProcess, weak_client_ui_, destination_port_, - base::Passed(&message))); + incoming_data_.substr(kMessageHeaderSize, message_size))); incoming_data_.erase(0, kMessageHeaderSize + message_size); } diff --git a/chrome/browser/extensions/api/messaging/native_message_process_host.h b/chrome/browser/extensions/api/messaging/native_message_process_host.h index ef339b2..987055f 100644 --- a/chrome/browser/extensions/api/messaging/native_message_process_host.h +++ b/chrome/browser/extensions/api/messaging/native_message_process_host.h @@ -15,15 +15,13 @@ #include "chrome/browser/extensions/api/messaging/native_process_launcher.h" #include "content/public/browser/browser_thread.h" -namespace base { -class ListValue; -} - namespace net { + class DrainableIOBuffer; class FileStream; class IOBuffer; class IOBufferWithSize; + } // namespace net namespace extensions { @@ -45,9 +43,8 @@ class NativeMessageProcessHost public: virtual ~Client() {} // Called on the UI thread. - virtual void PostMessageFromNativeProcess( - int port_id, - scoped_ptr<base::ListValue> message) = 0; + virtual void PostMessageFromNativeProcess(int port_id, + const std::string& message) = 0; virtual void CloseChannel(int port_id, const std::string& error_message) = 0; }; diff --git a/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc b/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc index d8fe2c4..6fc7d3a 100644 --- a/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc +++ b/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc @@ -104,15 +104,19 @@ class NativeMessagingTest : public ::testing::Test, virtual void PostMessageFromNativeProcess( int port_id, - scoped_ptr<base::ListValue> message_as_list) OVERRIDE { - // |message_as_list| should contain a single DictionaryValue. Extract it - // into |last_message_|. - ASSERT_EQ(1u, message_as_list->GetSize()); - base::Value* last_message_value = NULL; - message_as_list->Remove(0, &last_message_value); - ASSERT_EQ(base::Value::TYPE_DICTIONARY, last_message_value->GetType()); - last_message_.reset( - static_cast<base::DictionaryValue*>(last_message_value)); + const std::string& message) OVERRIDE { + last_message_ = message; + + // Parse the message. + base::Value* parsed = base::JSONReader::Read(message); + base::DictionaryValue* dict_value; + if (parsed && parsed->GetAsDictionary(&dict_value)) { + last_message_parsed_.reset(dict_value); + } else { + LOG(ERROR) << "Failed to parse " << message; + last_message_parsed_.reset(); + delete parsed; + } if (read_message_run_loop_) read_message_run_loop_->Quit(); @@ -146,7 +150,8 @@ class NativeMessagingTest : public ::testing::Test, base::FilePath user_data_dir_; scoped_ptr<base::RunLoop> read_message_run_loop_; content::TestBrowserThreadBundle thread_bundle_; - scoped_ptr<DictionaryValue> last_message_; + std::string last_message_; + scoped_ptr<base::DictionaryValue> last_message_parsed_; }; // Read a single message from a local file. @@ -163,26 +168,19 @@ TEST_F(NativeMessagingTest, SingleSendMessageRead) { read_message_run_loop_.reset(new base::RunLoop()); read_message_run_loop_->RunUntilIdle(); - if (!last_message_) { + if (last_message_.empty()) { read_message_run_loop_.reset(new base::RunLoop()); native_message_process_host_->ReadNowForTesting(); read_message_run_loop_->Run(); } - ASSERT_TRUE(last_message_); - - scoped_ptr<base::Value> kTestMessageAsValue( - base::JSONReader::Read(kTestMessage)); - ASSERT_TRUE(kTestMessageAsValue); - EXPECT_TRUE(base::Value::Equals(kTestMessageAsValue.get(), - last_message_.get())) - << "Expected " << *kTestMessageAsValue << " got " << *last_message_; + EXPECT_EQ(kTestMessage, last_message_); } // Tests sending a single message. The message should get written to // |temp_file| and should match the contents of single_message_request.msg. TEST_F(NativeMessagingTest, SingleSendMessageWrite) { base::FilePath temp_output_file = temp_dir_.path().AppendASCII("output"); - base::FilePath temp_input_file = CreateTempFileWithMessage("{}"); + base::FilePath temp_input_file = CreateTempFileWithMessage(std::string()); scoped_ptr<NativeProcessLauncher> launcher( new FakeLauncher(temp_input_file, temp_output_file)); @@ -228,29 +226,30 @@ TEST_F(NativeMessagingTest, EchoConnect) { native_message_process_host_->Send("{\"text\": \"Hello.\"}"); read_message_run_loop_.reset(new base::RunLoop()); read_message_run_loop_->Run(); - ASSERT_TRUE(last_message_); + ASSERT_FALSE(last_message_.empty()); + ASSERT_TRUE(last_message_parsed_); std::string expected_url = std::string("chrome-extension://") + kTestNativeMessagingExtensionId + "/"; int id; - EXPECT_TRUE(last_message_->GetInteger("id", &id)); + EXPECT_TRUE(last_message_parsed_->GetInteger("id", &id)); EXPECT_EQ(1, id); std::string text; - EXPECT_TRUE(last_message_->GetString("echo.text", &text)); + EXPECT_TRUE(last_message_parsed_->GetString("echo.text", &text)); EXPECT_EQ("Hello.", text); std::string url; - EXPECT_TRUE(last_message_->GetString("caller_url", &url)); + EXPECT_TRUE(last_message_parsed_->GetString("caller_url", &url)); EXPECT_EQ(expected_url, url); native_message_process_host_->Send("{\"foo\": \"bar\"}"); read_message_run_loop_.reset(new base::RunLoop()); read_message_run_loop_->Run(); - EXPECT_TRUE(last_message_->GetInteger("id", &id)); + EXPECT_TRUE(last_message_parsed_->GetInteger("id", &id)); EXPECT_EQ(2, id); - EXPECT_TRUE(last_message_->GetString("echo.foo", &text)); + EXPECT_TRUE(last_message_parsed_->GetString("echo.foo", &text)); EXPECT_EQ("bar", text); - EXPECT_TRUE(last_message_->GetString("caller_url", &url)); + EXPECT_TRUE(last_message_parsed_->GetString("caller_url", &url)); EXPECT_EQ(expected_url, url); } diff --git a/chrome/browser/extensions/message_handler.cc b/chrome/browser/extensions/message_handler.cc index f0576e5..8389b9a 100644 --- a/chrome/browser/extensions/message_handler.cc +++ b/chrome/browser/extensions/message_handler.cc @@ -4,7 +4,6 @@ #include "chrome/browser/extensions/message_handler.h" -#include "base/values.h" #include "chrome/browser/extensions/api/messaging/message_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/profiles/profile.h" @@ -44,12 +43,12 @@ void MessageHandler::RenderViewHostInitialized() { } void MessageHandler::OnPostMessage(int port_id, - const base::ListValue& message) { + const std::string& message) { Profile* profile = Profile::FromBrowserContext( render_view_host()->GetProcess()->GetBrowserContext()); MessageService* message_service = MessageService::Get(profile); if (message_service) { - message_service->PostMessage(port_id, make_scoped_ptr(message.DeepCopy())); + message_service->PostMessage(port_id, message); } } diff --git a/chrome/browser/extensions/message_handler.h b/chrome/browser/extensions/message_handler.h index 7142682..140105d 100644 --- a/chrome/browser/extensions/message_handler.h +++ b/chrome/browser/extensions/message_handler.h @@ -9,10 +9,6 @@ #include "content/public/browser/render_view_host_observer.h" -namespace base { -class ListValue; -} - namespace extensions { // Filters and dispatches extension-related IPC messages that arrive from @@ -37,7 +33,7 @@ class MessageHandler : public content::RenderViewHostObserver { private: // Message handlers. - void OnPostMessage(int port_id, const base::ListValue& message); + void OnPostMessage(int port_id, const std::string& message); DISALLOW_COPY_AND_ASSIGN(MessageHandler); }; diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 6c3cbf1..eb1de60 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -1710,6 +1710,7 @@ 'renderer/extensions/json_schema_unittest.cc', 'renderer/extensions/module_system_unittest.cc', 'renderer/extensions/renderer_permissions_policy_delegate_unittest.cc', + 'renderer/extensions/safe_builtins_unittest.cc', 'renderer/media/webrtc_logging_handler_impl_unittest.cc', 'renderer/net/predictor_queue_unittest.cc', 'renderer/net/renderer_predictor_unittest.cc', diff --git a/chrome/common/extensions/extension_messages.h b/chrome/common/extensions/extension_messages.h index 708f0f6..a49359b 100644 --- a/chrome/common/extensions/extension_messages.h +++ b/chrome/common/extensions/extension_messages.h @@ -426,7 +426,7 @@ IPC_MESSAGE_ROUTED4(ExtensionMsg_DispatchOnConnect, // Deliver a message sent with ExtensionHostMsg_PostMessage. IPC_MESSAGE_ROUTED2(ExtensionMsg_DeliverMessage, int /* target_port_id */, - base::ListValue /* message args, a 0-or-1 length list */) + std::string /* message */) // Dispatch the Port.onDisconnect event for message channels. IPC_MESSAGE_ROUTED2(ExtensionMsg_DispatchOnDisconnect, @@ -536,7 +536,7 @@ IPC_SYNC_MESSAGE_CONTROL4_1(ExtensionHostMsg_OpenChannelToTab, // by ViewHostMsg_OpenChannelTo*. IPC_MESSAGE_ROUTED2(ExtensionHostMsg_PostMessage, int /* port_id */, - base::ListValue /* message args, a 0-or-1 length list */) + std::string /* message */) // Send a message to an extension process. The handle is the value returned // by ViewHostMsg_OpenChannelTo*. diff --git a/chrome/renderer/extensions/dispatcher.cc b/chrome/renderer/extensions/dispatcher.cc index ec1b055..50348c3 100644 --- a/chrome/renderer/extensions/dispatcher.cc +++ b/chrome/renderer/extensions/dispatcher.cc @@ -510,7 +510,7 @@ void Dispatcher::OnDispatchOnConnect( } void Dispatcher::OnDeliverMessage(int target_port_id, - const base::ListValue& message) { + const std::string& message) { MiscellaneousBindings::DeliverMessage( v8_context_set_.GetAll(), target_port_id, diff --git a/chrome/renderer/extensions/dispatcher.h b/chrome/renderer/extensions/dispatcher.h index 2d81dff..80cd0ee 100644 --- a/chrome/renderer/extensions/dispatcher.h +++ b/chrome/renderer/extensions/dispatcher.h @@ -164,7 +164,7 @@ class Dispatcher : public content::RenderProcessObserver { const std::string& channel_name, const base::DictionaryValue& source_tab, const ExtensionMsg_ExternalConnectionInfo& info); - void OnDeliverMessage(int target_port_id, const base::ListValue& message); + void OnDeliverMessage(int target_port_id, const std::string& message); void OnDispatchOnDisconnect(int port_id, const std::string& error_message); void OnSetFunctionNames(const std::vector<std::string>& names); void OnSetSystemFont(const std::string& font_family, diff --git a/chrome/renderer/extensions/extension_helper.cc b/chrome/renderer/extensions/extension_helper.cc index a0b651a..2dc8dc6 100644 --- a/chrome/renderer/extensions/extension_helper.cc +++ b/chrome/renderer/extensions/extension_helper.cc @@ -271,9 +271,8 @@ void ExtensionHelper::OnExtensionDispatchOnConnect( render_view()); } -void ExtensionHelper::OnExtensionDeliverMessage( - int target_id, - const base::ListValue& message) { +void ExtensionHelper::OnExtensionDeliverMessage(int target_id, + const std::string& message) { MiscellaneousBindings::DeliverMessage(dispatcher_->v8_context_set().GetAll(), target_id, message, diff --git a/chrome/renderer/extensions/extension_helper.h b/chrome/renderer/extensions/extension_helper.h index 0d4f8b5..07a9b59 100644 --- a/chrome/renderer/extensions/extension_helper.h +++ b/chrome/renderer/extensions/extension_helper.h @@ -81,7 +81,7 @@ class ExtensionHelper const base::DictionaryValue& source_tab, const ExtensionMsg_ExternalConnectionInfo& info); void OnExtensionDeliverMessage(int target_port_id, - const base::ListValue& message); + const std::string& message); void OnExtensionDispatchOnDisconnect(int port_id, const std::string& error_message); void OnExecuteCode(const ExtensionMsg_ExecuteCode_Params& params); diff --git a/chrome/renderer/extensions/miscellaneous_bindings.cc b/chrome/renderer/extensions/miscellaneous_bindings.cc index 753c9bb..8921d77 100644 --- a/chrome/renderer/extensions/miscellaneous_bindings.cc +++ b/chrome/renderer/extensions/miscellaneous_bindings.cc @@ -95,9 +95,10 @@ class ExtensionImpl : public extensions::ChromeV8Extension { if (!renderview) return; - // Arguments are (int32 port_id, object message). - CHECK_EQ(2, args.Length()); - CHECK(args[0]->IsInt32()); + // Arguments are (int32 port_id, string message). + CHECK(args.Length() == 2 && + args[0]->IsInt32() && + args[1]->IsString()); int port_id = args[0]->Int32Value(); if (!HasPortData(port_id)) { @@ -106,18 +107,8 @@ class ExtensionImpl : public extensions::ChromeV8Extension { return; } - // The message can be any base::Value but IPC can't serialize that, so we - // give it a singleton base::ListValue instead, or an empty list if the - // argument was undefined (v8 value converter will return NULL for this). - scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); - scoped_ptr<base::Value> message( - converter->FromV8Value(args[1], context()->v8_context())); - ListValue message_as_list; - if (message) - message_as_list.Append(message.release()); - renderview->Send(new ExtensionHostMsg_PostMessage( - renderview->GetRoutingID(), port_id, message_as_list)); + renderview->GetRoutingID(), port_id, *v8::String::AsciiValue(args[1]))); } // Forcefully disconnects a port. @@ -284,7 +275,7 @@ void MiscellaneousBindings::DispatchOnConnect( void MiscellaneousBindings::DeliverMessage( const ChromeV8ContextSet::ContextSet& contexts, int target_port_id, - const base::ListValue& message, + const std::string& message, content::RenderView* restrict_to_render_view) { v8::HandleScope handle_scope; @@ -300,9 +291,6 @@ void MiscellaneousBindings::DeliverMessage( if ((*it)->v8_context().IsEmpty()) continue; - v8::Handle<v8::Context> context = (*it)->v8_context(); - v8::Context::Scope context_scope(context); - // Check to see whether the context has this port before bothering to create // the message. v8::Handle<v8::Value> port_id_handle = v8::Integer::New(target_port_id); @@ -316,19 +304,7 @@ void MiscellaneousBindings::DeliverMessage( continue; std::vector<v8::Handle<v8::Value> > arguments; - - // Convert the message to a v8 object; either a value or undefined. - // See PostMessage for more details. - if (message.empty()) { - arguments.push_back(v8::Undefined()); - } else { - CHECK_EQ(1u, message.GetSize()); - const base::Value* message_value = NULL; - message.Get(0, &message_value); - scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create()); - arguments.push_back(converter->ToV8Value(message_value, context)); - } - + arguments.push_back(v8::String::New(message.c_str(), message.size())); arguments.push_back(port_id_handle); (*it)->module_system()->CallModuleMethod("miscellaneous_bindings", "dispatchOnMessage", diff --git a/chrome/renderer/extensions/miscellaneous_bindings.h b/chrome/renderer/extensions/miscellaneous_bindings.h index 95d0733..79dfeb7 100644 --- a/chrome/renderer/extensions/miscellaneous_bindings.h +++ b/chrome/renderer/extensions/miscellaneous_bindings.h @@ -11,7 +11,6 @@ namespace base { class DictionaryValue; -class ListValue; } namespace content { @@ -56,7 +55,7 @@ class MiscellaneousBindings { static void DeliverMessage( const ChromeV8ContextSet::ContextSet& context_set, int target_port_id, - const base::ListValue& message, + const std::string& message, content::RenderView* restrict_to_render_view); // Dispatches the onDisconnect event in response to the channel being closed. @@ -67,6 +66,6 @@ class MiscellaneousBindings { content::RenderView* restrict_to_render_view); }; -} // namespace +} // namespace extensions #endif // CHROME_RENDERER_EXTENSIONS_MISCELLANEOUS_BINDINGS_H_ diff --git a/chrome/renderer/extensions/module_system.cc b/chrome/renderer/extensions/module_system.cc index c29b440..29575b2 100644 --- a/chrome/renderer/extensions/module_system.cc +++ b/chrome/renderer/extensions/module_system.cc @@ -198,6 +198,7 @@ v8::Handle<v8::Value> ModuleSystem::RequireForJsInner( // Each safe builtin. Keep in order with the arguments in WrapSource. context_->safe_builtins()->GetArray(), context_->safe_builtins()->GetFunction(), + context_->safe_builtins()->GetJSON(), context_->safe_builtins()->GetObjekt(), }; { @@ -482,7 +483,7 @@ v8::Handle<v8::String> ModuleSystem::WrapSource(v8::Handle<v8::String> source) { // Keep in order with the arguments in RequireForJsInner. v8::Handle<v8::String> left = v8::String::New( "(function(require, requireNative, exports," - "$Array, $Function, $Object) {" + "$Array, $Function, $JSON, $Object) {" "'use strict';"); v8::Handle<v8::String> right = v8::String::New("\n})"); return handle_scope.Close( diff --git a/chrome/renderer/extensions/safe_builtins.cc b/chrome/renderer/extensions/safe_builtins.cc index c28b077..05f67fc 100644 --- a/chrome/renderer/extensions/safe_builtins.cc +++ b/chrome/renderer/extensions/safe_builtins.cc @@ -28,6 +28,7 @@ const char kClassName[] = "extensions::SafeBuiltins"; // This is a convenient way to save functions that user scripts may clobber.\n" const char kScript[] = "(function() {\n" + "'use strict';\n" "native function Apply();\n" "native function Save();\n" "\n" @@ -52,16 +53,64 @@ const char kScript[] = " });\n" "}\n" "\n" - "function getSafeBuiltin(builtin) {\n" - " var safe = {};\n" + "function getSafeBuiltin(builtin, includePrototype) {\n" + " var safe = function() {\n" + " throw 'Safe objects cannot be called nor constructed. ' +\n" + " 'Use $Foo.self() or new $Foo.self() instead.';\n" + " };\n" + " safe.self = builtin;\n" " makeCallable(builtin, safe, true);\n" - " makeCallable(builtin.prototype, safe, false);\n" + " if (includePrototype)\n" + " makeCallable(builtin.prototype, safe, false);\n" " return safe;\n" "}\n" "\n" - "[Array, Function, Object].forEach(function(builtin) {\n" - " Save(builtin.name, getSafeBuiltin(builtin));\n" + "// Built-in types taken from the ECMAScript spec. The spec may change,\n" + "// though. It would be nice to generate this somehow.\n" + "// Note: no JSON, it needs to handle toJSON being overriden so is\n" + "// implemented by hand.\n" + "var builtinTypes = [Object, Function, Array, String, Boolean, Number,\n" + " /*Math,*/ Date, RegExp, /*JSON,*/ Error, EvalError,\n" + " ReferenceError, SyntaxError, TypeError, URIError];\n" + "builtinTypes.forEach(function(builtin) {\n" + " Save(builtin.name, getSafeBuiltin(builtin, true));\n" "});\n" + "Save('Math', getSafeBuiltin(Math, false));\n" + "// Save JSON. This is trickier because extensions can override toJSON in\n" + "// incompatible ways, and we need to prevent that.\n" + "var builtinToJSONs = builtinTypes.map(function(t) {\n" + " return t.toJSON;\n" + "});\n" + "var builtinArray = Array;\n" + "var builtinJSONStringify = JSON.stringify;\n" + "Save('JSON', {\n" + " parse: JSON.parse,\n" + " stringify: function(obj) {\n" + " var savedToJSONs = new builtinArray(builtinTypes.length);\n" + " try {\n" + " for (var i = 0; i < builtinTypes.length; ++i) {\n" + " try {\n" + " if (builtinTypes[i].prototype.toJSON !==\n" + " builtinToJSONs[i]) {\n" + " savedToJSONs[i] = builtinTypes[i].prototype.toJSON;\n" + " builtinTypes[i].prototype.toJSON = builtinToJSONs[i];\n" + " }\n" + " } catch (e) {}\n" + " }\n" + " } catch (e) {}\n" + " try {\n" + " return builtinJSONStringify(obj);\n" + " } finally {\n" + " for (var i = 0; i < builtinTypes.length; ++i) {\n" + " try {\n" + " if (i in savedToJSONs)\n" + " builtinTypes[i].prototype.toJSON = savedToJSONs[i];\n" + " } catch (e) {}\n" + " }\n" + " }\n" + " }\n" + "});\n" + "\n" "}());\n"; v8::Local<v8::String> MakeKey(const char* name) { @@ -157,6 +206,10 @@ v8::Local<v8::Object> SafeBuiltins::GetFunction() const { return Load("Function", context_->v8_context()); } +v8::Local<v8::Object> SafeBuiltins::GetJSON() const { + return Load("JSON", context_->v8_context()); +} + v8::Local<v8::Object> SafeBuiltins::GetObjekt() const { return Load("Object", context_->v8_context()); } diff --git a/chrome/renderer/extensions/safe_builtins.h b/chrome/renderer/extensions/safe_builtins.h index c2dcb4d..3be54c1 100644 --- a/chrome/renderer/extensions/safe_builtins.h +++ b/chrome/renderer/extensions/safe_builtins.h @@ -30,6 +30,7 @@ class SafeBuiltins { // Object.keys.call(...) becomes Object.keys(...) v8::Local<v8::Object> GetArray() const; v8::Local<v8::Object> GetFunction() const; + v8::Local<v8::Object> GetJSON() const; // NOTE(kalman): VS2010 won't compile "GetObject", it mysteriously renames it // to "GetObjectW" - hence GetObjekt. Sorry. v8::Local<v8::Object> GetObjekt() const; diff --git a/chrome/renderer/extensions/safe_builtins_unittest.cc b/chrome/renderer/extensions/safe_builtins_unittest.cc new file mode 100644 index 0000000..effc087 --- /dev/null +++ b/chrome/renderer/extensions/safe_builtins_unittest.cc @@ -0,0 +1,68 @@ +// Copyright 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. + +#include "chrome/test/base/module_system_test.h" + +#include "grit/renderer_resources.h" + +namespace extensions { +namespace { + +class SafeBuiltinsUnittest : public ModuleSystemTest { +}; + +TEST_F(SafeBuiltinsUnittest, TestNotOriginalObject) { + ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); + RegisterModule("test", + "var assert = requireNative('assert');\n" + "Array.foo = 10;\n" + "assert.AssertTrue(!$Array.hasOwnProperty('foo'));\n" + ); + module_system_->Require("test"); +} + +TEST_F(SafeBuiltinsUnittest, TestSelf) { + ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); + RegisterModule("test", + "var assert = requireNative('assert');\n" + "Array.foo = 10;\n" + "assert.AssertTrue($Array.self.foo == 10);\n" + "var arr = $Array.self(1);\n" + "assert.AssertTrue(arr.length == 1);\n" + "assert.AssertTrue(arr[0] === undefined);\n" + ); + module_system_->Require("test"); +} + +TEST_F(SafeBuiltinsUnittest, TestStaticFunction) { + ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); + RegisterModule("test", + "var assert = requireNative('assert');\n" + "Object.getOwnPropertyNames = function() {throw new Error()};\n" + "var obj = {a: 10};\n" + "var propertyNames = $Object.getOwnPropertyNames(obj);\n" + "assert.AssertTrue(propertyNames.length == 1);\n" + "assert.AssertTrue(propertyNames[0] == 'a');\n" + ); + module_system_->Require("test"); +} + +TEST_F(SafeBuiltinsUnittest, TestInstanceMethod) { + ModuleSystem::NativesEnabledScope natives_enabled_scope(module_system_.get()); + RegisterModule("test", + "var assert = requireNative('assert');\n" + "Array.prototype.push = function() {throw new Error();}\n" + "var arr = []\n" + "$Array.push(arr, 1);\n" + "assert.AssertTrue(arr.length == 1);\n" + "assert.AssertTrue(arr[0] == 1);\n" + ); + module_system_->Require("test"); +} + +// NOTE: JSON is already tested in ExtensionApiTest.Messaging, via +// chrome/test/data/extensions/api_test/messaging/connect/page.js. + +} // namespace +} // namespace extensions diff --git a/chrome/renderer/resources/extensions/miscellaneous_bindings.js b/chrome/renderer/resources/extensions/miscellaneous_bindings.js index 32f4c85b..52a7550 100644 --- a/chrome/renderer/resources/extensions/miscellaneous_bindings.js +++ b/chrome/renderer/resources/extensions/miscellaneous_bindings.js @@ -45,7 +45,10 @@ // Sends a message asynchronously to the context on the other end of this // port. Port.prototype.postMessage = function(msg) { - miscNatives.PostMessage(this.portId_, msg); + // JSON.stringify doesn't support a root object which is undefined. + if (msg === undefined) + msg = null; + miscNatives.PostMessage(this.portId_, $JSON.stringify(msg)); }; // Disconnects the port from the other end. @@ -266,8 +269,11 @@ // Called by native code when a message has been sent to the given port. function dispatchOnMessage(msg, portId) { var port = ports[portId]; - if (port) + if (port) { + if (msg) + msg = $JSON.parse(msg); port.onMessage.dispatch(msg, port); + } }; // Shared implementation used by tabs.sendMessage and runtime.sendMessage. diff --git a/chrome/test/data/devtools/extensions/devtools_messaging/devtools.js b/chrome/test/data/devtools/extensions/devtools_messaging/devtools.js index 915273b..56c2f14 100644 --- a/chrome/test/data/devtools/extensions/devtools_messaging/devtools.js +++ b/chrome/test/data/devtools/extensions/devtools_messaging/devtools.js @@ -30,7 +30,7 @@ function step1() { } function step2() { - var object = [{"string": "foo"}, {"number": 42}]; + var object = { "string": "foo", "number": 42 }; chrome.extension.sendRequest(object, function(response) { assertEquals('onRequest callback: ' + JSON.stringify(object), response); step3(); |