diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/renderer | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer')
43 files changed, 9780 insertions, 0 deletions
diff --git a/chrome/renderer/SConscript b/chrome/renderer/SConscript new file mode 100644 index 0000000..e9b83a8 --- /dev/null +++ b/chrome/renderer/SConscript @@ -0,0 +1,99 @@ +# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Import('env')
+
+env = env.Clone()
+
+
+
+#/Yu"precompiled_wtl.h"
+#/Fp"C:\src\trunk\chrome\Debug\obj\browser\precompiled_wtl.pch"
+#/Fo"C:\src\trunk\chrome\Debug\obj\browser\\"
+#/Fd"C:\src\trunk\chrome\Debug\obj\browser\vc80.pdb"
+#/W3
+#/WX
+#/FI "precompiled_wtl.h"
+
+env.Prepend(
+ CPPPATH = [
+ env.Dir('../app/resources'),
+ env.Dir('#/tools/build/win'),
+ env.Dir('#/..'),
+ ],
+ CPPDEFINES = [
+ 'U_STATIC_IMPLEMENTATION',
+ 'CERT_CHAIN_PARA_HAS_EXTRA_FIELDS',
+ 'WIN32_LEAN_AND_MEAN',
+ ],
+ CCFLAGS = [
+ '/TP',
+
+ '/Wp64',
+
+ '/wd4503',
+ '/wd4819',
+ ],
+)
+
+env.Append(
+ CPPPATH = [
+ 'third_party/wtl/include',
+ '$ICU38_DIR/public/common',
+ '$ICU38_DIR/public/i18n',
+ #Dir('#../chrome/Debug/obj/generated_resources'),
+ #Dir('#../chrome/Debug/obj/localized_strings'),
+ '$NPAPI_DIR',
+ '$SKIA_DIR/include',
+ '$SKIA_DIR/include/corecg',
+ '$SKIA_DIR/platform',
+ ],
+)
+
+input_files = [
+ 'about_handler.cc',
+ 'automation/dom_automation_controller.cc',
+ 'debug_message_handler.cc',
+ 'dom_ui_bindings.cc',
+ 'external_js_object.cc',
+ 'localized_error.cc',
+ 'net/render_dns_master.cc',
+ 'net/render_dns_queue.cc',
+ 'plugin_channel_host.cc',
+ 'render_process.cc',
+ 'render_thread.cc',
+ 'render_view.cc',
+ 'render_widget.cc',
+ 'renderer_glue.cc',
+ 'renderer_main.cc',
+ 'visitedlink_slave.cc',
+ 'webplugin_delegate_proxy.cc',
+]
+
+env.StaticLibrary('renderer', input_files)
diff --git a/chrome/renderer/about_handler.cc b/chrome/renderer/about_handler.cc new file mode 100644 index 0000000..5a8b7a0 --- /dev/null +++ b/chrome/renderer/about_handler.cc @@ -0,0 +1,93 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> + +#include "chrome/renderer/about_handler.h" + +#include "googleurl/src/gurl.h" + +struct AboutHandlerUrl { + char *url; + void (*action)(); +}; + +static AboutHandlerUrl about_urls[] = { + { "about:crash", AboutHandler::AboutCrash }, + { "about:hang", AboutHandler::AboutHang }, + { "about:shorthang", AboutHandler::AboutShortHang }, + { NULL, NULL } +}; + +bool AboutHandler::WillHandle(const GURL& url) { + if (url.scheme() != "about") + return false; + + struct AboutHandlerUrl* url_handler = about_urls; + while (url_handler->url) { + if (url == GURL(url_handler->url)) + return true; + url_handler++; + } + return false; +} + +// static +bool AboutHandler::MaybeHandle(const GURL& url) { + if (url.scheme() != "about") + return false; + + struct AboutHandlerUrl* url_handler = about_urls; + while (url_handler->url) { + if (url == GURL(url_handler->url)) { + url_handler->action(); + return true; // theoretically :] + } + url_handler++; + } + return false; +} + +// static +void AboutHandler::AboutCrash() { + int *zero = NULL; + *zero = 0; // Null pointer dereference: kaboom! +} + +// static +void AboutHandler::AboutHang() { + for (;;) { + Sleep(1000); + } +} + +// static +void AboutHandler::AboutShortHang() { + Sleep(20000); +} diff --git a/chrome/renderer/about_handler.h b/chrome/renderer/about_handler.h new file mode 100644 index 0000000..1d545a0 --- /dev/null +++ b/chrome/renderer/about_handler.h @@ -0,0 +1,68 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Contains code for handling "about:" URLs in the renderer process. We handle +// most about: URLs in the browser process (see +// browser/browser_about_handler.*), but test URLs like about:crash need to +// happen in the renderer. + +#ifndef CHROME_RENDERER_ABOUT_HANDLER_H__ +#define CHROME_RENDERER_ABOUT_HANDLER_H__ + +#include "base/basictypes.h" + +class GURL; + +class AboutHandler { + public: + // Given a URL, determine whether or not to handle it specially. Returns + // true if the URL was handled. + static bool MaybeHandle(const GURL& url); + + // Returns true if the URL is one that this AboutHandler will handle when + // MaybeHandle is called. + static bool WillHandle(const GURL& url); + + // Induces a renderer crash. + static void AboutCrash(); + + // Induces a renderer hang. + static void AboutHang(); + + // Induces a brief (20 second) hang to make sure hang monitors go away. + static void AboutShortHang(); + + private: + AboutHandler(); + ~AboutHandler(); + + DISALLOW_EVIL_CONSTRUCTORS(AboutHandler); +}; + +#endif // CHROME_RENDERER_ABOUT_HANDLER_H__ diff --git a/chrome/renderer/automation/dom_automation_controller.cc b/chrome/renderer/automation/dom_automation_controller.cc new file mode 100644 index 0000000..4d6dca3 --- /dev/null +++ b/chrome/renderer/automation/dom_automation_controller.cc @@ -0,0 +1,125 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/renderer/automation/dom_automation_controller.h" + +#include "chrome/common/json_value_serializer.h" +#include "chrome/common/render_messages.h" +#include "base/string_util.h" + +IPC::Message::Sender* DomAutomationController::sender_(NULL); +int DomAutomationController::routing_id_(MSG_ROUTING_NONE); +int DomAutomationController::automation_id_(MSG_ROUTING_NONE); + +DomAutomationController::DomAutomationController(){ + BindMethod("send", &DomAutomationController::send); + BindMethod("setAutomationId", &DomAutomationController::setAutomationId); +} + +void DomAutomationController::send(const CppArgumentList& args, + CppVariant* result) { + if (args.size() != 1) { + result->SetNull(); + return; + } + + if (automation_id_ == MSG_ROUTING_NONE) { + result->SetNull(); + return; + } + + std::string json; + JSONStringValueSerializer serializer(&json); + Value* value = NULL; + + // Warning: note that JSON officially requires the root-level object to be + // an object (e.g. {foo:3}) or an array, while here we're serializing + // strings, bools, etc. to "JSON". This only works because (a) the JSON + // writer is lenient, and (b) on the receiving side we wrap the JSON string + // in square brackets, converting it to an array, then parsing it and + // grabbing the 0th element to get the value out. + switch(args[0].type) { + case NPVariantType_String: { + value = Value::CreateStringValue(UTF8ToWide(args[0].ToString())); + break; + } + case NPVariantType_Bool: { + value = Value::CreateBooleanValue(args[0].ToBoolean()); + break; + } + case NPVariantType_Int32: { + value = Value::CreateIntegerValue(args[0].ToInt32()); + break; + } + case NPVariantType_Double: { + // The value that is sent back is an integer while it is treated + // as a double in this binding. The reason being that KJS treats + // any number value as a double. Refer for more details, + // chrome/third_party/webkit/src/JavaScriptCore/bindings/c/c_utility.cpp + value = Value::CreateIntegerValue(args[0].ToInt32()); + break; + } + default: { + result->SetNull(); + return; + } + } + + bool succeeded = serializer.Serialize(*value); + if (!succeeded) { + result->SetNull(); + return; + } + + succeeded = sender_->Send( + new ViewHostMsg_DomOperationResponse(routing_id_, json, automation_id_)); + + automation_id_ = MSG_ROUTING_NONE; + + result->Set(succeeded); + return; +} + +void DomAutomationController::setAutomationId( + const CppArgumentList& args, CppVariant* result) { + if (args.size() != 1) { + result->SetNull(); + return; + } + + // The check here is for NumberType and not Int32 as + // KJS::JSType only defines a NumberType (no Int32) + if (!args[0].isNumber()) { + result->SetNull(); + return; + } + + automation_id_ = args[0].ToInt32(); + result->Set(true); +} diff --git a/chrome/renderer/automation/dom_automation_controller.h b/chrome/renderer/automation/dom_automation_controller.h new file mode 100644 index 0000000..d7ac44f --- /dev/null +++ b/chrome/renderer/automation/dom_automation_controller.h @@ -0,0 +1,133 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_RENDERER_AUTOMATION_DOM_AUTOMATION_CONTROLLER_H__ +#define CHROME_RENDERER_AUTOMATION_DOM_AUTOMATION_CONTROLLER_H__ + +#include "chrome/common/ipc_message.h" +#include "webkit/glue/cpp_bound_class.h" + +/* DomAutomationController class: + Bound to Javascript window.domAutomationController object. + At the very basic, this object makes any native value (string, numbers, + boolean) from javascript available to the automation host in Cpp. + Any renderer implementation that is built with this binding will allow the + above facility. + The intended use of this object is to expose the DOM Objects and their + attributes to the automation host. + + A typical usage would be like following (JS code): + + var object = document.getElementById('some_id'); + window.domAutomationController.send(object.nodeName); // get the tag name + + For the exact mode of usage, + refer AutomationProxyTest.*DomAutomationController tests. + + The class provides a single send method that can send variety of native + javascript values. (NPString, Number(double), Boolean) + + The actual communication occurs in the following manner: + + TEST MASTER RENDERER + (1) (3) + |AProxy| ----->|AProvider|----->|RenderView|------| + /\ | | | + | | | | + |(6) |(2) |(0) |(4) + | | \/ | + | |-------->|DAController|<----| + | | + | |(5) + |---------|WebContents|<----------| + + + Legends: + - AProxy = AutomationProxy + - AProvider = AutomationProvider + - DAController = DomAutomationController + + (0) Initialization step where DAController is bound to the renderer + and the view_id of the renderer is supplied to the DAController for + routing message in (5). (routing_id_) + (1) A 'javascript:' url is sent from the test process to master as an IPC + message. A unique routing id is generated at this stage (automation_id_) + (2) The automation_id_ of step (1) is supplied to DAController by calling + the bound method setAutomationId(). This is required for routing message + in (6). + (3) The 'javascript:' url is sent for execution by calling into + Browser::LoadURL() + (4) A callback is generated as a result of domAutomationController.send() + into Cpp. The supplied value is received as a result of this callback. + (5) The value received in (4) is sent to the master along with the + stored automation_id_ as an IPC message. routing_id_ is used to route + the message. (IPC messages, ViewHostMsg_*DomAutomation* ) + (6) The value and the automation_id_ is extracted out of the message received + in (5). This value is relayed to AProxy using another IPC message. + automation_id_ is used to route the message. + (IPC messages, AutomationMsg_Dom*Response) + +*/ + +// TODO(vibhor): Add another method-pair like sendLater() and sendNow() +// sendLater() should keep building a json serializer +// sendNow() should send the above serializer as a string. +class DomAutomationController : public CppBoundClass { + public: + DomAutomationController(); + ~DomAutomationController() {} + + // Makes the renderer send a javascript value to the app. + // The value to be sent can be either of type NPString, + // Number (double casted to int32) or boolean. + // The function returns true/false based on the result of actual send over + // IPC. It sets the return value to null on unexpected errors or arguments. + void send(const CppArgumentList& args, CppVariant* result); + + void setAutomationId(const CppArgumentList& args, CppVariant* result); + + // TODO(vibhor): Implement later + // static CppBindingObjectMethod sendLater; + // static CppBindingObjectMethod sendNow; + + static void set_routing_id(int routing_id) { routing_id_ = routing_id; } + + static void set_message_sender(IPC::Message::Sender* sender) { + sender_ = sender; + } + + private: + static IPC::Message::Sender* sender_; + + // Refer to the comments at the top of the file for more details. + static int routing_id_; // routing id to be used by first channel. + static int automation_id_; // routing id to be used by the next channel. +}; + +#endif // CHROME_RENDERER_AUTOMATION_DOM_AUTOMATION_CONTROLLER_H__ diff --git a/chrome/renderer/debug_message_handler.cc b/chrome/renderer/debug_message_handler.cc new file mode 100644 index 0000000..60df1b2 --- /dev/null +++ b/chrome/renderer/debug_message_handler.cc @@ -0,0 +1,142 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/renderer/debug_message_handler.h" +#include "chrome/renderer/render_view.h" + +//////////////////////////////////////// +// methods called from the RenderThread + +DebugMessageHandler::DebugMessageHandler(RenderView* view) : + debugger_(NULL), view_(view), channel_(NULL) { + view_loop_ = MessageLoop::current(); + view_routing_id_ = view_->routing_id(); +} + +DebugMessageHandler::~DebugMessageHandler() { +} + +void DebugMessageHandler::EvaluateScriptUrl(const std::wstring& url) { + DCHECK(MessageLoop::current() == view_loop_); + // It's possible that this will get cleared out from under us. + RenderView* view = view_; + if (view) { + view->EvaluateScriptUrl(L"", url); + } +} + +/////////////////////////////////////////////// +// all methods below called from the IO thread + +void DebugMessageHandler::DebuggerOutput(const std::wstring& out) { + DCHECK(MessageLoop::current() != view_loop_); + // When certain commands are sent to the debugger, but we're not paused, + // it's possible that no JS is running, so these commands can't be processed. + // In these cases, we force some trivial, no side-effect, JS to be executed + // so that V8 can process the commands. + if (((out == L"break set") || (out == L"request queued")) && view_loop_) { + if (view_loop_) { + view_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &DebugMessageHandler::EvaluateScriptUrl, + std::wstring(L"javascript:void(0)"))); + } + } else if (channel_) { + channel_->Send(new ViewHostMsg_DebuggerOutput(view_routing_id_, out)); + } +} + +void DebugMessageHandler::OnAttach() { + DebuggerOutput(L"{'type':'event', 'event':'attach'}"); + debugger_->Attach(); +} + +void DebugMessageHandler::OnSendToDebugger(const std::wstring& cmd) { + if (!debugger_) { + debugger_ = new Debugger(this); + if (cmd == L"" || cmd == L"attach") { + OnAttach(); + } else { + NOTREACHED(); + std::wstring msg = + StringPrintf(L"before attach, ignored command (%S)", cmd.c_str()); + DebuggerOutput(msg); + } + } else if (cmd == L"attach") { + OnAttach(); + } else if (cmd == L"quit" || cmd == L"detach") { + OnDetach(); + } else { + debugger_->Command(cmd); + } +} + +void DebugMessageHandler::OnDetach() { + if (debugger_) + debugger_->Detach(); + if (view_loop_ && view_) + view_loop_->PostTask(FROM_HERE, NewRunnableMethod( + view_, &RenderView::OnDebugDetach)); +} + +void DebugMessageHandler::OnFilterAdded(IPC::Channel* channel) { + channel_ = channel; +} + +void DebugMessageHandler::OnFilterRemoved() { + channel_ = NULL; + view_loop_ = NULL; + view_ = NULL; + // By the time this is called, the view is not in a state where it can + // receive messages from the MessageLoop, so we need to clear those first. + OnDetach(); +} + +bool DebugMessageHandler::OnMessageReceived(const IPC::Message& message) { + DCHECK(channel_ != NULL); + + // In theory, there could be multiple debuggers running (in practice this + // hasn't been implemented yet), so make sure we only handle messages meant + // for the view we were initialized for. + if (message.routing_id() != view_routing_id_) + return false; + + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(DebugMessageHandler, message) + IPC_MESSAGE_HANDLER(ViewMsg_SendToDebugger, OnSendToDebugger); + // If the debugger is active, then it's possible that the renderer thread + // is suspended handling a breakpoint. In that case, the renderer will + // hang forever and never exit. To avoid this, we look for close messages + // and tell the debugger to shutdown. + IPC_MESSAGE_HANDLER_GENERIC(ViewMsg_Close, + if (debugger_) OnDetach(); + handled = false;) + IPC_MESSAGE_UNHANDLED(handled = false); + IPC_END_MESSAGE_MAP() + return handled; +} diff --git a/chrome/renderer/debug_message_handler.h b/chrome/renderer/debug_message_handler.h new file mode 100644 index 0000000..87d5d9c --- /dev/null +++ b/chrome/renderer/debug_message_handler.h @@ -0,0 +1,82 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// MessageFilter object to handle messages aimed at the debugger, and to +// dispatch them from the main thread rather than the render thread. +// Also owns the reference to the Debugger object and handles callbacks from it. + +#ifndef CHROME_RENDERER_DEBUG_MESSAGE_HANDLER_H_ +#define CHROME_RENDERER_DEBUG_MESSAGE_HANDLER_H_ + +#include "chrome/common/ipc_channel_proxy.h" +#include "webkit/glue/debugger.h" + +class RenderView; + +class DebugMessageHandler : public IPC::ChannelProxy::MessageFilter, + public Debugger::Delegate { + public: + DebugMessageHandler(RenderView* view); + virtual ~DebugMessageHandler(); + + private: + // evaluate javascript URL in the renderer + void EvaluateScriptUrl(const std::wstring& url); + + // Debugger::Delegate callback method to handle debugger output. + void DebuggerOutput(const std::wstring& out); + + // Sends a command to the debugger. + void OnSendToDebugger(const std::wstring& cmd); + + // Sends an attach event to the debugger front-end. + void OnAttach(); + + // Unregister with V8 and notify the RenderView. + void OnDetach(); + + virtual void OnFilterAdded(IPC::Channel* channel); + virtual void OnFilterRemoved(); + + // Returns true to indicate that the message was handled, or false to let + // the message be handled in the default way. + virtual bool OnMessageReceived(const IPC::Message& message); + + scoped_refptr<Debugger> debugger_; + + // Don't ever dereference view_ directly from another thread as it's not + // threadsafe, instead proxy locally via its MessageLoop. + RenderView* view_; + MessageLoop* view_loop_; + + int32 view_routing_id_; + IPC::Channel* channel_; +}; + +#endif // CHROME_RENDERER_DEBUG_MESSAGE_HANDLER_H_
\ No newline at end of file diff --git a/chrome/renderer/dom_ui_bindings.cc b/chrome/renderer/dom_ui_bindings.cc new file mode 100644 index 0000000..e1574c1 --- /dev/null +++ b/chrome/renderer/dom_ui_bindings.cc @@ -0,0 +1,82 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/renderer/dom_ui_bindings.h" + +#include "base/json_writer.h" +#include "base/values.h" +#include "chrome/common/render_messages.h" +#include "chrome/common/stl_util-inl.h" + +DOMUIBindings::DOMUIBindings() : routing_id_(0) { + BindMethod("send", &DOMUIBindings::send); +} + +DOMUIBindings::~DOMUIBindings() { + STLDeleteContainerPointers(properties_.begin(), properties_.end()); +} + +void DOMUIBindings::send(const CppArgumentList& args, CppVariant* result) { + // We expect at least a string message identifier, and optionally take + // an object parameter. If we get anything else we bail. + if (args.size() < 1 || args.size() > 2) + return; + + // Require the first parameter to be the message name. + if (!args[0].isString()) + return; + const std::string message = args[0].ToString(); + + // If they've provided an optional message parameter, convert that into JSON. + std::string content; + if (args.size() == 2) { + if (!args[1].isObject()) + return; + // TODO(evanm): we ought to support more than just sending arrays of + // strings, but it's not yet necessary for the current code. + std::vector<std::wstring> strings = args[1].ToStringVector(); + ListValue value; + for (size_t i = 0; i < strings.size(); ++i) { + value.Append(Value::CreateStringValue(strings[i])); + } + JSONWriter::Write(&value, /* pretty_print= */ false, &content); + } + + // Send the message up to the browser. + sender_->Send( + new ViewHostMsg_DOMUISend(routing_id_, message, content)); +} + +void DOMUIBindings::SetProperty(const std::string& name, + const std::string& value) { + CppVariant* cpp_value = new CppVariant; + cpp_value->Set(value); + BindProperty(name, cpp_value); + properties_.push_back(cpp_value); +} diff --git a/chrome/renderer/dom_ui_bindings.h b/chrome/renderer/dom_ui_bindings.h new file mode 100644 index 0000000..a7736e3 --- /dev/null +++ b/chrome/renderer/dom_ui_bindings.h @@ -0,0 +1,76 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_RENDERER_DOM_UI_BINDINGS_H__ +#define CHROME_RENDERER_DOM_UI_BINDINGS_H__ + +#include "chrome/common/ipc_message.h" +#include "webkit/glue/cpp_bound_class.h" + +// DOMUIBindings is the class backing the "chrome" object accessible +// from Javascript from privileged pages. +// +// We expose one function, for sending a message to the browser: +// send(String name, Object argument); +// It's plumbed through to the OnDOMUIMessage callback on RenderViewHost +// delegate. +class DOMUIBindings : public CppBoundClass { + public: + DOMUIBindings(); + ~DOMUIBindings(); + + // The send() function provided to Javascript. + void send(const CppArgumentList& args, CppVariant* result); + + // Set the message channel back to the browser. + void set_message_sender(IPC::Message::Sender* sender) { + sender_ = sender; + } + + // Set the routing id for messages back to the browser. + void set_routing_id(int routing_id) { + routing_id_ = routing_id; + } + + // Sets a property with the given name and value. + void SetProperty(const std::string& name, const std::string& value); + + private: + // Our channel back to the browser is a message sender + // and routing id. + IPC::Message::Sender* sender_; + int routing_id_; + + // The list of properties that have been set. We keep track of this so we + // can free them on destruction. + typedef std::vector<CppVariant*> PropertyList; + PropertyList properties_; +}; + +#endif // CHROME_RENDERER_DOM_UI_BINDINGS_H__ diff --git a/chrome/renderer/external_js_object.cc b/chrome/renderer/external_js_object.cc new file mode 100644 index 0000000..dc88d29 --- /dev/null +++ b/chrome/renderer/external_js_object.cc @@ -0,0 +1,48 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/renderer/external_js_object.h" + +#include "chrome/renderer/render_view.h" + +ExternalJSObject::ExternalJSObject() : render_view_(NULL) { + BindMethod("AddSearchProvider", &ExternalJSObject::AddSearchProvider); +} + +void ExternalJSObject::AddSearchProvider(const CppArgumentList& args, + CppVariant* result) { + DCHECK(render_view_); + result->SetNull(); + + if (render_view_) { + if (args.size() < 1 || !args[0].isString()) + return; + render_view_->AddSearchProvider(args[0].ToString()); + } +} diff --git a/chrome/renderer/external_js_object.h b/chrome/renderer/external_js_object.h new file mode 100644 index 0000000..3c43729 --- /dev/null +++ b/chrome/renderer/external_js_object.h @@ -0,0 +1,66 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* + ExternalJSObject class: + Bound to a JavaScript window.external object using + CppBoundClass::BindToJavascript(), this adds methods accessible from JS for + compatibility with other browsers. +*/ + +#ifndef CHROME_RENDERER_EXTERNAL_JS_OBJECT_H__ +#define CHROME_RENDERER_EXTERNAL_JS_OBJECT_H__ + +#include "base/basictypes.h" +#include "webkit/glue/cpp_bound_class.h" + +class RenderView; + +class ExternalJSObject : public CppBoundClass { + public: + // Builds the property and method lists needed to bind this class to a JS + // object. + ExternalJSObject(); + + // A RenderView must be set before AddSearchProvider is called, or the call + // will do nothing. + void set_render_view(RenderView* rv) { render_view_ = rv; } + + // Given a URL to an OpenSearch document in the first argument, adds the + // corresponding search provider as a keyword search. The nonstandard + // capitalization is for compatibility with Firefox and IE. + void AddSearchProvider(const CppArgumentList& args, CppVariant* result); + + private: + RenderView* render_view_; + + DISALLOW_EVIL_CONSTRUCTORS(ExternalJSObject); +}; + +#endif // CHROME_RENDERER_EXTERNAL_JS_OBJECT_H__ diff --git a/chrome/renderer/localized_error.cc b/chrome/renderer/localized_error.cc new file mode 100644 index 0000000..261ba5b --- /dev/null +++ b/chrome/renderer/localized_error.cc @@ -0,0 +1,229 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/renderer/localized_error.h" + +#include "base/logging.h" +#include "base/string_util.h" +#include "base/values.h" +#include "chrome/common/l10n_util.h" +#include "googleurl/src/gurl.h" +#include "net/base/escape.h" +#include "net/base/net_errors.h" +#include "webkit/glue/weberror.h" + +#include "generated_resources.h" + +namespace { + +// Helper method for generating the google cache lookup url. +const std::wstring ConstructGoogleCacheUrl(const std::wstring& url) { + // TODO(tc): use locale based google domain + std::wstring cache_url(L"http://www.google.com/search?q=cache:"); + cache_url.append(EscapeQueryParamValueUTF8(url)); + return cache_url; +} + +enum NAV_SUGGESTIONS { + SUGGEST_NONE = 0, + SUGGEST_RELOAD = 1 << 0, + SUGGEST_CACHE = 1 << 1, + SUGGEST_HOSTNAME = 1 << 2, +}; + +struct WebErrorNetErrorMap { + const int error_code; + const unsigned int title_resource_id; + const unsigned int heading_resource_id; + const unsigned int summary_resource_id; + const unsigned int details_resource_id; + const int suggestions; // Bitmap of SUGGEST_* values. +}; + +WebErrorNetErrorMap net_error_options[] = { + {net::ERR_TIMED_OUT, + IDS_ERRORPAGES_TITLE_NOT_AVAILABLE, + IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, + IDS_ERRORPAGES_SUMMARY_NOT_AVAILABLE, + IDS_ERRORPAGES_DETAILS_TIMED_OUT, + SUGGEST_RELOAD | SUGGEST_CACHE, + }, + {net::ERR_CONNECTION_FAILED, + IDS_ERRORPAGES_TITLE_NOT_AVAILABLE, + IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, + IDS_ERRORPAGES_SUMMARY_NOT_AVAILABLE, + IDS_ERRORPAGES_DETAILS_CONNECT_FAILED, + SUGGEST_RELOAD | SUGGEST_CACHE, + }, + {net::ERR_NAME_NOT_RESOLVED, + IDS_ERRORPAGES_TITLE_NOT_AVAILABLE, + IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, + IDS_ERRORPAGES_SUMMARY_NOT_AVAILABLE, + IDS_ERRORPAGES_DETAILS_NAME_NOT_RESOLVED, + SUGGEST_RELOAD | SUGGEST_CACHE, + }, + {net::ERR_INTERNET_DISCONNECTED, + IDS_ERRORPAGES_TITLE_NOT_AVAILABLE, + IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, + IDS_ERRORPAGES_SUMMARY_NOT_AVAILABLE, + IDS_ERRORPAGES_DETAILS_DISCONNECTED, + SUGGEST_RELOAD, + }, + {net::ERR_FILE_NOT_FOUND, + IDS_ERRORPAGES_TITLE_NOT_FOUND, + IDS_ERRORPAGES_HEADING_NOT_FOUND, + IDS_ERRORPAGES_SUMMARY_NOT_FOUND, + IDS_ERRORPAGES_DETAILS_FILE_NOT_FOUND, + SUGGEST_NONE, + }, + {net::ERR_TOO_MANY_REDIRECTS, + IDS_ERRORPAGES_TITLE_LOAD_FAILED, + IDS_ERRORPAGES_HEADING_TOO_MANY_REDIRECTS, + IDS_ERRORPAGES_SUMMARY_TOO_MANY_REDIRECTS, + IDS_ERRORPAGES_DETAILS_TOO_MANY_REDIRECTS, + SUGGEST_RELOAD, + }, +}; + +} // namespace + +void GetLocalizedErrorValues(const WebError& error, + DictionaryValue* error_strings) { + // Grab strings that are applicable to all error pages + error_strings->SetString(L"detailsLink", + l10n_util::GetString(IDS_ERRORPAGES_DETAILS_LINK)); + error_strings->SetString(L"detailsHeading", + l10n_util::GetString(IDS_ERRORPAGES_DETAILS_HEADING)); + + // Grab the strings and settings that depend on the error type. Init + // options with default values. + WebErrorNetErrorMap options = { + 0, + IDS_ERRORPAGES_TITLE_NOT_AVAILABLE, + IDS_ERRORPAGES_HEADING_NOT_AVAILABLE, + IDS_ERRORPAGES_SUMMARY_NOT_AVAILABLE, + IDS_ERRORPAGES_DETAILS_UNKNOWN, + SUGGEST_NONE, + }; + int error_code = error.GetErrorCode(); + for (int i = 0; i < arraysize(net_error_options); ++i) { + if (net_error_options[i].error_code == error_code) { + memcpy(&options, &net_error_options[i], sizeof(WebErrorNetErrorMap)); + break; + } + } + + std::wstring suggestions_heading; + if (options.suggestions != SUGGEST_NONE) { + suggestions_heading = + l10n_util::GetString(IDS_ERRORPAGES_SUGGESTION_HEADING); + } + error_strings->SetString(L"suggestionsHeading", suggestions_heading); + + std::wstring failed_url(UTF8ToWide(error.GetFailedURL().spec())); + // URLs are always LTR. + if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) + l10n_util::WrapStringWithLTRFormatting(&failed_url); + error_strings->SetString(L"title", + l10n_util::GetStringF(options.title_resource_id, + failed_url.c_str())); + error_strings->SetString(L"heading", + l10n_util::GetString(options.heading_resource_id)); + + DictionaryValue* summary = new DictionaryValue; + summary->SetString(L"msg", + l10n_util::GetString(options.summary_resource_id)); + // TODO(tc): we want the unicode url here since it's being displayed + summary->SetString(L"failedUrl", failed_url.c_str()); + error_strings->Set(L"summary", summary); + + // Error codes are expected to be negative + DCHECK(error_code < 0); + std::wstring details = l10n_util::GetString(options.details_resource_id); + error_strings->SetString(L"details", + l10n_util::GetStringF(IDS_ERRORPAGES_DETAILS_TEMPLATE, + IntToWString(-error_code), + ASCIIToWide(net::ErrorToString(error_code)), + details)); + + if (options.suggestions & SUGGEST_RELOAD) { + DictionaryValue* suggest_reload = new DictionaryValue; + suggest_reload->SetString(L"msg", + l10n_util::GetString(IDS_ERRORPAGES_SUGGESTION_RELOAD)); + suggest_reload->SetString(L"reloadUrl", failed_url.c_str()); + error_strings->Set(L"suggestionsReload", suggest_reload); + } + + if (options.suggestions & SUGGEST_CACHE) { + DictionaryValue* suggest_cache = new DictionaryValue; + suggest_cache->SetString(L"msg", + l10n_util::GetString(IDS_ERRORPAGES_SUGGESTION_CACHE)); + suggest_cache->SetString(L"cacheUrl", + ConstructGoogleCacheUrl(failed_url).c_str()); + error_strings->Set(L"suggestionsCache", suggest_cache); + } + + if (options.suggestions & SUGGEST_HOSTNAME) { + // Only show the "Go to hostname" suggestion if the failed_url has a path. + const GURL& failed_url = error.GetFailedURL(); + if (std::string() == failed_url.path()) { + DictionaryValue* suggest_home_page = new DictionaryValue; + suggest_home_page->SetString(L"suggestionsHomepageMsg", + l10n_util::GetString(IDS_ERRORPAGES_SUGGESTION_HOMEPAGE)); + std::wstring homepage(UTF8ToWide(failed_url.GetWithEmptyPath().spec())); + // URLs are always LTR. + if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) + l10n_util::WrapStringWithLTRFormatting(&homepage); + suggest_home_page->SetString(L"homePage", homepage); + // TODO(tc): we actually want the unicode hostname + suggest_home_page->SetString(L"hostName", + UTF8ToWide(failed_url.host())); + error_strings->Set(L"suggestionsHomepage", suggest_home_page); + } + } +} + +void GetFormRepostErrorValues(const GURL& display_url, + DictionaryValue* error_strings) { + std::wstring failed_url(UTF8ToWide(display_url.spec())); + // URLs are always LTR. + if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) + l10n_util::WrapStringWithLTRFormatting(&failed_url); + error_strings->SetString( + L"title", l10n_util::GetStringF(IDS_ERRORPAGES_TITLE_NOT_AVAILABLE, + failed_url.c_str())); + error_strings->SetString(L"heading", + l10n_util::GetString(IDS_HTTP_POST_WARNING_TITLE)); + error_strings->SetString(L"suggestionsHeading", L""); + DictionaryValue* summary = new DictionaryValue; + summary->SetString(L"msg", + l10n_util::GetString(IDS_ERRORPAGES_HTTP_POST_WARNING)); + error_strings->Set(L"summary", summary); +} + diff --git a/chrome/renderer/localized_error.h b/chrome/renderer/localized_error.h new file mode 100644 index 0000000..4db6735 --- /dev/null +++ b/chrome/renderer/localized_error.h @@ -0,0 +1,47 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_RENDERER_LOCALIZED_ERROR_VALUES_H__ +#define CHROME_RENDERER_LOCALIZED_ERROR_VALUES_H__ + +class DictionaryValue; +class WebError; +class GURL; + +void GetLocalizedErrorValues(const WebError& error, + DictionaryValue* error_strings); + +// Fills |error_strings| with values to be used to build an error page which +// warns against reposting form data. This is special cased because the form +// repost "error page" has no real error associated with it, and doesn't have +// enough strings localized to meaningfully fill the net error template. +void GetFormRepostErrorValues(const GURL& display_url, + DictionaryValue* error_strings); + +#endif // CHROME_RENDERER_LOCALIZED_ERROR_VALUES_H__ diff --git a/chrome/renderer/net/render_dns_master.cc b/chrome/renderer/net/render_dns_master.cc new file mode 100644 index 0000000..01f8c78 --- /dev/null +++ b/chrome/renderer/net/render_dns_master.cc @@ -0,0 +1,189 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// See header file for description of RenderDnsMaster class + + +#include "chrome/renderer/net/render_dns_master.h" + +#include <ctype.h> + +#include "base/logging.h" +#include "chrome/common/net/dns.h" +#include "chrome/common/render_messages.h" +#include "chrome/renderer/net/render_dns_queue.h" +#include "chrome/renderer/render_thread.h" + +// This API is used in the render process by renderer_glue.cc. +// IF you are in the render process, you MUST be on the renderer thread to call. +void DnsPrefetchCString(const char* hostname, size_t length) { + RenderThread::current()->Resolve(hostname, length); +} + +// The number of hostnames submitted to Browser DNS resolver per call to +// SubmitHostsnames() (which reads names from our queue). +static const size_t kMAX_SUBMISSION_PER_TASK = 30; + +RenderDnsMaster::RenderDnsMaster() + : c_string_queue_(1000), +#pragma warning(push) +#pragma warning(suppress: 4355) // Okay to pass "this" here. + render_dns_factory_(this) { +#pragma warning(pop) + Reset(); +} + +void RenderDnsMaster::Reset() { + domain_map_.clear(); + c_string_queue_.Clear(); + buffer_full_discard_count_ = 0; + numeric_ip_discard_count_ = 0; + new_name_count_ = 0; +} + +// Push names into queue quickly! +void RenderDnsMaster::Resolve(const char* name, size_t length) { + if (!length) + return; // Don't store empty strings in buffer. + if (is_numeric_ip(name, length)) + return; // Numeric IPs have no DNS lookup significance. + + size_t old_size = c_string_queue_.Size(); + DnsQueue::PushResult result = c_string_queue_.Push(name, length); + if (DnsQueue::SUCCESSFUL_PUSH == result) { + if (1 == c_string_queue_.Size()) { + DCHECK(0 == old_size); + if (0 != old_size) + return; // Overkill safety net: Don't send too many InvokeLater's. + render_dns_factory_.RevokeAll(); + RenderThread::current()->message_loop()->PostDelayedTask(FROM_HERE, + render_dns_factory_.NewRunnableMethod( + &RenderDnsMaster::SubmitHostnames), 10); + } + return; + } + if (DnsQueue::OVERFLOW_PUSH == result) { + buffer_full_discard_count_++; + return; + } + DCHECK(DnsQueue::REDUNDANT_PUSH == result); +} + +// Extract data from the Queue, and then send it off the the Browser process +// to be resolved. +void RenderDnsMaster::SubmitHostnames() { + // Get all names out of the C_string_queue (into our map) + ExtractBufferedNames(); + // TBD: IT could be that we should only extract about as many names as we are + // going to send to the browser. That would cause a "silly" page with a TON + // of URLs to start to overrun the DnsQueue, which will cause the names to + // be dropped (not stored in the queue). By fetching ALL names, we are + // taking on a lot of work, which may take a long time to process... perhaps + // longer than the page may be visible!?!?! If we implement a better + // mechanism for doing domain_map.clear() (see end of this method), then + // we'd automatically flush such pending work from a ridiculously link-filled + // page. + + // Don't overload the browser DNS lookup facility, or take too long here, + // by only sending off kMAX_SUBMISSION_PER_TASK names to the Browser. + // This will help to avoid overloads when a page has a TON of links. + DnsPrefetchNames(kMAX_SUBMISSION_PER_TASK); + if (new_name_count_ > 0 || 0 < c_string_queue_.Size()) { + render_dns_factory_.RevokeAll(); + RenderThread::current()->message_loop()->PostDelayedTask(FROM_HERE, + render_dns_factory_.NewRunnableMethod( + &RenderDnsMaster::SubmitHostnames), 10); + } else { + // TODO(JAR): Should we only clear the map when we navigate, or reload? + domain_map_.clear(); + } +} + +// Pull some hostnames from the queue, and add them to our map. +void RenderDnsMaster::ExtractBufferedNames(size_t size_goal) { + size_t count(0); // Number of entries to find (0 means find all). + if (size_goal > 0) { + if (size_goal <= domain_map_.size()) + return; // Size goal was met. + count = size_goal - domain_map_.size(); + } + + std::string name; + while (c_string_queue_.Pop(&name)) { + DCHECK(0 != name.size()); + // We don't put numeric IP names into buffer. + DCHECK(!is_numeric_ip(name.c_str(), name.size())); + DomainUseMap::iterator it; + it = domain_map_.find(name); + if (domain_map_.end() == it) { + domain_map_[name] = kPending; + new_name_count_++; + if (0 == count) continue; // Until buffer is empty. + if (1 == count) break; // We found size_goal. + DCHECK(1 < count); + count--; + } else { + DCHECK(kPending == it->second || kLookupRequested == it->second); + } + } +} + +void RenderDnsMaster::DnsPrefetchNames(size_t max_count) { + // We are on the renderer thread, and just need to send things to the browser. + chrome_common_net::NameList names; + for (DomainUseMap::iterator it = domain_map_.begin(); + it != domain_map_.end(); + it++) { + if (0 == (it->second & kLookupRequested)) { + it->second |= kLookupRequested; + names.push_back(it->first); + if (0 == max_count) continue; // Get all, independent of count. + if (1 == max_count) break; + max_count--; + DCHECK(1 <= max_count); + } + } + new_name_count_ -= names.size(); + DCHECK(new_name_count_ >= 0); + + RenderThread::current()->Send(new ViewHostMsg_DnsPrefetch(names)); +} + +// is_numeric_ip() checks to see if all characters in name are either numeric, +// or dots. Such a name will not actually be passed to DNS, as it is an IP +// address. +bool RenderDnsMaster::is_numeric_ip(const char* name, size_t length) { + // Scan for a character outside our lookup list. + while (length-- > 0) { + if (!isdigit(*name) && '.' != *name) + return false; + name++; + } + return true; +} diff --git a/chrome/renderer/net/render_dns_master.h b/chrome/renderer/net/render_dns_master.h new file mode 100644 index 0000000..31f26fe --- /dev/null +++ b/chrome/renderer/net/render_dns_master.h @@ -0,0 +1,134 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A RenderDnsMaster instance is maintained for each RenderThread. +// Hostnames are typically added to the embedded queue during rendering. +// The first addition to the queue (transitioning from empty to having +// some names) causes a processing task to be added to the Renderer Thread. +// The processing task gathers all buffered names, and send them via IPC +// to the browser, so that DNS lookups can be performed before the user attempts +// to traverse a link. +// This class removed some duplicates, and discards numeric IP addresss +// (which wouldn't looked up in DNS anyway). +// To limit the time during the processing task (and avoid stalling the Render +// thread), several limits are placed on how much of the queue to process. +// If the processing task is not able to completely empty the queue, it +// schedules a future continuation of the task, and keeps the map of already +// sent names. If the entire queue is processed, then the list of "sent names" +// is cleared so that future gatherings may again pass along the same names. + +#ifndef CHROME_RENDERER_RENDER_NET_DNS_MASTER_H__ +#define CHROME_RENDERER_RENDER_NET_DNS_MASTER_H__ + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/task.h" +#include "chrome/renderer/net/render_dns_queue.h" + +// Global API consists to do Prefetching in renderer. This uses IPC to reach +// the Browser's global functions. +void DnsPrefetchCString(const char* hostname, size_t length); + +class RenderDnsMaster { + public: + RenderDnsMaster(); + + ~RenderDnsMaster() {} + + // Push a name into the queue to be resolved. + void Resolve(const char* name, size_t length); + + // SubmitHosts processes the buffered names, and submits them for DNS + // prefetching. + // Note that browser process may decide which names should be looked up (to + // pre-warm the cache) based on what has been (or not been) looked up + // recently. + // If sending for DNS lookup is incomplete (queue is not empty, or not all + // names in map are sent, or ...) then a task to continue processing is + // sent to our thread loop. + void SubmitHostnames(); + + // The following is private, but exposed for testing purposes only. + static bool RenderDnsMaster::is_numeric_ip(const char* name, size_t length); + + private: + // ExtractBufferedNames pulls names from queue into the map, reducing or + // eliminating a waiting queue. + // The size_goal argument can be used to reduce the amount of + // processing done in this method, and can leave some data + // in the buffer under some circumstances. + // If size_goal is zero, then extraction proceeds until + // the queue is empty. If size goal is positive, then + // extraction continues until the domain_map_ contains + // at least the specified number of names, or the buffer is empty. + void ExtractBufferedNames(size_t size_goal = 0); + + // DnsPrefetchNames does not check the buffer, and just sends names + // that are already collected in the domain_map_ for DNS lookup. + // If max_count is zero, then all available names are sent; and + // if positive, then at most max_count names will be sent. + void DnsPrefetchNames(size_t max_count = 0); + + // Reset() restores initial state provided after construction. + // This discards ALL queue entries, and map entries. + void Reset(); + + // We use c_string_queue_ to hold lists of names supplied typically) by the + // renderer. It queues the names, at minimal cost to the renderer's thread, + // and allows this class to process them when time permits (in a later task). + DnsQueue c_string_queue_; + + + // domain_map_ contains (for each domain) one of the next two constants, + // depending on whether we have asked the browser process to do the actual + // DNS lookup. + static const int kLookupRequested = 0x1; + static const int kPending = 0x0; + typedef std::map<std::string, int> DomainUseMap; + DomainUseMap domain_map_; + + // Cache a tally of the count of names that haven't yet been sent + // for DNS pre-fetching. Note that we *could* recalculate this + // count by iterating over domain_map_, looking for even values. + size_t new_name_count_; + + // We have some metrics to examine performance. We might use + // these metrics to modify buffer counts etc. some day. + int buffer_full_discard_count_; + int numeric_ip_discard_count_; + + ScopedRunnableMethodFactory<RenderDnsMaster> render_dns_factory_; + + DISALLOW_EVIL_CONSTRUCTORS(RenderDnsMaster); +}; // class RenderDnsMaster + +#endif // CHROME_RENDERER_RENDER_NET_DNS_MASTER_H__ diff --git a/chrome/renderer/net/render_dns_master_unittest.cc b/chrome/renderer/net/render_dns_master_unittest.cc new file mode 100644 index 0000000..beaddf7 --- /dev/null +++ b/chrome/renderer/net/render_dns_master_unittest.cc @@ -0,0 +1,61 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Single threaded tests of RenderDnsMaster functionality. + +#include "chrome/renderer/net/render_dns_master.h" +#include "testing/gtest/include/gtest/gtest.h" + +#include <algorithm> + +namespace { + +class RenderDnsMasterTest : public testing::Test { +}; + +TEST(RenderDnsMasterTest, NumericIpDiscardCheck) { + // Regular names. + const std::string A("a.com"), B("b.net"), C("www.other.uk"); + // Combination of digits plus dots. + const std::string N1("1.3."), N2("5.5.7.12"); + +#define TESTNAME(string) RenderDnsMaster::is_numeric_ip((string.data()), \ + (string).size()) + + EXPECT_TRUE(TESTNAME(N1)); + EXPECT_TRUE(TESTNAME(N2)); + + EXPECT_FALSE(TESTNAME(A)); + EXPECT_FALSE(TESTNAME(B)); + EXPECT_FALSE(TESTNAME(C)); + +#undef TESTNAME +} + +} // namespace anonymous
\ No newline at end of file diff --git a/chrome/renderer/net/render_dns_queue.cc b/chrome/renderer/net/render_dns_queue.cc new file mode 100644 index 0000000..6a1661c --- /dev/null +++ b/chrome/renderer/net/render_dns_queue.cc @@ -0,0 +1,169 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// See header file for description of DnsQueue class + +#include "chrome/renderer/net/render_dns_queue.h" + +#include "base/logging.h" +#include "base/stats_counters.h" + +DnsQueue::DnsQueue(BufferSize size) + : buffer_(new char[size + 2]), + buffer_size_(size + 1), + buffer_sentinel_(size + 1), + size_(0) { + CHECK(0 < static_cast<BufferSize>(size + 3)); // Avoid overflow worries. + buffer_[buffer_sentinel_] = '\0'; // Guard byte to help reading data. + readable_ = writeable_ = 0; // Buffer starts empty. +} + +DnsQueue::~DnsQueue(void) { +} + +// Push takes an unterminated string plus its length. +// The string must not contain a null terminator. +// Exactly length chars are written, or nothing is written. +// Returns true for success, false there was no room to push. +DnsQueue::PushResult DnsQueue::Push(const char* source, + const size_t unsigned_length) { + BufferSize length = static_cast<BufferSize>(unsigned_length); + if (0 > length+1) // Avoid overflows in conversion to signed. + return OVERFLOW_PUSH; + + // To save on sites with a LOT of links to the SAME domain, we have a + // a compaction hack that removes duplicates when we try to push() a + // match with the last push. + if (0 < size_ && readable_ + length < buffer_sentinel_ && + 0 == strncmp(source, &buffer_[readable_], unsigned_length) && + '\0' == buffer_[readable_ + unsigned_length]) { + SIMPLE_STATS_COUNTER(L"DNS.PrefetchDnsRedundantPush"); + + // We already wrote this name to the queue, so we'll skip this repeat. + return REDUNDANT_PUSH; + } + + // Calling convention precludes nulls. + DCHECK(!length || '\0' != source[length - 1]); + + DCHECK(Validate()); + + BufferSize available_space = readable_ - writeable_; + + if (0 >= available_space) { + available_space += buffer_size_; + } + + if (length + 1 >= available_space) { + SIMPLE_STATS_COUNTER(L"DNS.PrefetchDnsQueueFull"); + return OVERFLOW_PUSH; // Not enough space to push. + } + + BufferSize dest = writeable_; + BufferSize space_till_wrap = buffer_sentinel_ - dest; + if (space_till_wrap < length + 1) { + // Copy until we run out of room at end of buffer. + std::memcpy(&buffer_[dest], source, space_till_wrap); + // Ensure caller didn't have embedded '\0' and also + // ensure trailing sentinel was in place. + DCHECK(space_till_wrap == strlen(&buffer_[dest])); // Relies on sentinel. + + length -= space_till_wrap; + source += space_till_wrap; + dest = 0; // Continue writing at start of buffer. + } + + // Copy any remaining portion of source. + std::memcpy(&buffer_[dest], source, length); + DCHECK(dest + length < buffer_sentinel_); + buffer_[dest + length] = '\0'; // We need termination in our buffer. + DCHECK(length == strlen(&buffer_[dest])); // Preclude embedded '\0'. + + dest += length + 1; + if (dest == buffer_sentinel_) + dest = 0; + + writeable_ = dest; + size_++; + DCHECK(Validate()); + return SUCCESSFUL_PUSH; +} + +// Extracts the next available string from the buffer. +// The returned string is null terminated, and hence has length +// that is exactly one greater than the written string. +// If the buffer is empty, then the Pop and returns false. +bool DnsQueue::Pop(std::string* out_string) { + DCHECK(Validate()); + // Sentinel will preclude memory reads beyond buffer's end. + DCHECK('\0' == buffer_[buffer_sentinel_]); + + if (readable_ == writeable_) { + return false; // buffer was empty + } + + // Constructor *may* rely on sentinel for null termination. + (*out_string) = &buffer_[readable_]; + // Our sentinel_ at end of buffer precludes an overflow in cast. + BufferSize first_fragment_size = static_cast<BufferSize> (out_string->size()); + + BufferSize terminal_null; + if (readable_ + first_fragment_size >= buffer_sentinel_) { + // Sentinel was used, so we need the portion after the wrap. + out_string->append(&buffer_[0]); // Fragment at start of buffer. + // Sentinel precludes overflow in cast to signed type. + terminal_null = static_cast<BufferSize>(out_string->size()) + - first_fragment_size; + } else { + terminal_null = readable_ + first_fragment_size; + } + DCHECK('\0' == buffer_[terminal_null]); + + BufferSize new_readable = terminal_null + 1; + if (buffer_sentinel_ == new_readable) + new_readable = 0; + + readable_ = new_readable; + size_--; + if (readable_ == writeable_ || 0 == size_) { + // Queue is empty, so reset to start of buffer to help with peeking. + readable_ = writeable_ = 0; + } + DCHECK(Validate()); + return true; +} + +bool DnsQueue::Validate() { + return (readable_ >= 0) && + readable_ < buffer_sentinel_ && + writeable_ >= 0 && + writeable_ < buffer_sentinel_ && + '\0' == buffer_[buffer_sentinel_] && + ((0 == size_) == (readable_ == writeable_)); +} diff --git a/chrome/renderer/net/render_dns_queue.h b/chrome/renderer/net/render_dns_queue.h new file mode 100644 index 0000000..0a180f6 --- /dev/null +++ b/chrome/renderer/net/render_dns_queue.h @@ -0,0 +1,117 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// DnsQueue is implemented as an almost FIFO circular buffer for text +// strings that don't have embedded nulls ('\0'). The "almost" element is that +// some duplicate strings may be removed (i.e., the string won't really be +// pushed *if* the class happens to notice that a duplicate is already in the +// queue). +// The buffers internal format is null terminated character strings +// (a.k.a., c_strings). +// It is written to be as fast as possible during push() operations, so +// that there will be minimal performance impact on a supplier thread. +// The push() operation will not block, and no memory allocation is involved +// (internally) during the push operations. +// The one caveat is that if there is insufficient space in the buffer to +// accept additional string via a push(), then the push() will fail, and +// the buffer will be unmodified. + +// This class was designed for use in DNS prefetch operations. During +// rendering, the supplier is the renderer (typically), and the consumer +// is a thread that sends messages to an async DNS resolver. + +#ifndef CHROME_RENDERER_NET_RENDER_DNS_QUEUE_H__ +#define CHROME_RENDERER_NET_RENDER_DNS_QUEUE_H__ + +#include <string> + +#include "base/basictypes.h" +#include "base/lock.h" +#include "base/scoped_ptr.h" + +class DnsQueue { + public: + // BufferSize is a signed type used for indexing into a buffer. + typedef int32 BufferSize; + + enum PushResult { SUCCESSFUL_PUSH, OVERFLOW_PUSH, REDUNDANT_PUSH }; + + // The size specified in the constructor creates a buffer large enough + // to hold at most one string of that length, or "many" + // strings of considerably shorter length. Note that strings + // are padded internally with a terminal '\0" while stored, + // so if you are trying to be precise and get N strings of + // length K to fit, you should actually construct a buffer with + // an internal size of N*(K+1). + explicit DnsQueue(BufferSize size); + ~DnsQueue(void); + + size_t Size() const { return size_; } + void Clear() { + size_ = 0; + readable_ = writeable_; + Validate(); + } + + // Push takes an unterminated string of the given length + // and inserts it into the queue for later + // extraction by read. For each successful push(), there + // can later be a corresponding read() to extracted the text. + // The string must not contain an embedded null terminator + // Exactly length chars are written, or the push fails (where + // "fails" means nothing is written). + // Returns true for success, false for failure (nothing written). + PushResult Push(const char* source, const size_t length); + + PushResult Push(std::string source) { + return Push(source.c_str(), source.length()); + } + + // Extract the next available string from the buffer. + // If the buffer is empty, then return false. + bool Pop(std::string* out_string); + + private: + bool Validate(); // Checks that all internal data is valid. + + const BufferSize buffer_size_; // Size one smaller than allocated space. + const scoped_array<char> buffer_; // Circular buffer, plus extra char ('\0'). + const BufferSize buffer_sentinel_; // Index of extra '\0' at end of buffer_. + + // If writable_ == readable_, then the buffer is empty. + BufferSize readable_; // Next readable char in buffer_. + BufferSize writeable_; // The next space in buffer_ to push. + + // Number of queued strings + size_t size_; + + DISALLOW_EVIL_CONSTRUCTORS(DnsQueue); +}; // class DnsQueue + +#endif // CHROME_RENDERER_NET_RENDER_DNS_QUEUE_H__ diff --git a/chrome/renderer/net/render_dns_queue_unittest.cc b/chrome/renderer/net/render_dns_queue_unittest.cc new file mode 100644 index 0000000..2829264 --- /dev/null +++ b/chrome/renderer/net/render_dns_queue_unittest.cc @@ -0,0 +1,287 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <sstream> + +#include "chrome/renderer/net/render_dns_queue.h" +#include "testing/gtest/include/gtest/gtest.h" + +// Single threaded tests of DnsQueue functionality. + +namespace { + +class DnsQueueTest : public testing::Test { +}; + +// Define a helper class that does Push'es and Pop's of numbers. +// This makes it easy to test a LOT of reads, and keep the expected Pop +// value in sync with the Push value. +class DnsQueueSequentialTester { + public: + DnsQueueSequentialTester::DnsQueueSequentialTester( + DnsQueue& buffer, int32 read_counter = 0, int32 write_counter = 0); + + // Return of false means buffer was full, or would not take entry. + bool Push(void); // Push the string value of next number. + + // Return of false means buffer returned wrong value. + bool Pop(void); // Validate string value of next read. + + private: + DnsQueue* buffer_; + int32 read_counter_; // expected value of next read string. + int32 write_counter_; // Numerical value to write next string. + DISALLOW_EVIL_CONSTRUCTORS(DnsQueueSequentialTester); +}; + + +DnsQueueSequentialTester::DnsQueueSequentialTester( + DnsQueue& buffer, int32 read_counter, int32 write_counter) + : buffer_(&buffer), + read_counter_(read_counter), + write_counter_(write_counter) { +} + +bool DnsQueueSequentialTester::Push(void) { + std::ostringstream value; + value << write_counter_; + + // Exercise both write methods intermittently. + DnsQueue::PushResult result = (write_counter_ % 2) ? + buffer_->Push(value.str().c_str(), value.str().size()) : + buffer_->Push(value.str()); + if (DnsQueue::SUCCESSFUL_PUSH == result) + write_counter_++; + return DnsQueue::OVERFLOW_PUSH != result; +} + +bool DnsQueueSequentialTester::Pop(void) { + std::string string; + if (buffer_->Pop(&string)) { + std::ostringstream expected_value; + expected_value << read_counter_++; + EXPECT_STREQ(expected_value.str().c_str(), string.c_str()) + << "Pop did not match write for value " << read_counter_; + return true; + } + return false; +} + + +TEST(DnsQueueTest, BufferUseCheck) { + // Use a small buffer so we can see that we can't write a string as soon as it + // gets longer than one less than the buffer size. The extra empty character + // is used to keep read and write pointers from overlapping when buffer is + // full. This shows the buffer size can constrain writes (and we're not + // scribbling all over memory). + const int buffer_size = 3; // Just room for 2 digts plus '\0' plus blank. + std::string string; + DnsQueue buffer(buffer_size); + DnsQueueSequentialTester tester(buffer); + + EXPECT_FALSE(tester.Pop()) << "Pop from empty buffer succeeded"; + + int i; + for (i = 0; i < 102; i++) { + if (!tester.Push()) + break; // String was too large. + EXPECT_TRUE(tester.Pop()) << "Unable to read back data " << i; + EXPECT_FALSE(buffer.Pop(&string)) + << "read from empty buffer not flagged"; + } + + EXPECT_GE(i, 100) << "Can't write 2 digit strings in 4 character buffer"; + EXPECT_LT(i, 101) << "We wrote 3 digit strings into a 4 character buffer"; +} + +TEST(DnsQueueTest, SubstringUseCheck) { + // Verify that only substring is written/read. + const int buffer_size = 100; + const char big_string[] = "123456789"; + std::string string; + DnsQueue buffer(buffer_size); + + EXPECT_FALSE(buffer.Pop(&string)) << "Initial buffer not empty"; + + EXPECT_EQ(DnsQueue::SUCCESSFUL_PUSH, buffer.Push(big_string, 3)) + << "Can't write string"; + EXPECT_EQ(DnsQueue::SUCCESSFUL_PUSH, buffer.Push(big_string, 0)) + << "Can't write null string"; + EXPECT_EQ(DnsQueue::SUCCESSFUL_PUSH, buffer.Push(big_string, 5)) + << "Can't write string"; + + EXPECT_TRUE(buffer.Pop(&string)) << "Filled buffer marked as empty"; + EXPECT_STREQ(string.c_str(), "123") << "Can't read actual data"; + EXPECT_TRUE(buffer.Pop(&string)) << "Filled buffer marked as empty"; + EXPECT_STREQ(string.c_str(), "") << "Can't read null string"; + EXPECT_TRUE(buffer.Pop(&string)) << "Filled buffer marked as empty"; + EXPECT_STREQ(string.c_str(), "12345") << "Can't read actual data"; + + EXPECT_FALSE(buffer.Pop(&string)) + << "read from empty buffer not flagged"; +} + +TEST(DnsQueueTest, SizeCheck) { + // Verify that size is correctly accounted for in buffer. + const int buffer_size = 100; + std::string input_string = "Hello"; + std::string string; + DnsQueue buffer(buffer_size); + + EXPECT_EQ(0, buffer.Size()); + EXPECT_FALSE(buffer.Pop(&string)); + EXPECT_EQ(DnsQueue::SUCCESSFUL_PUSH, buffer.Push(input_string)); + EXPECT_EQ(1, buffer.Size()); + EXPECT_EQ(DnsQueue::SUCCESSFUL_PUSH, buffer.Push("Hi There")); + EXPECT_EQ(2, buffer.Size()); + EXPECT_TRUE(buffer.Pop(&string)); + EXPECT_EQ(1, buffer.Size()); + EXPECT_TRUE(buffer.Pop(&string)); + EXPECT_EQ(0, buffer.Size()); + EXPECT_EQ(DnsQueue::SUCCESSFUL_PUSH, buffer.Push(input_string)); + EXPECT_EQ(1, buffer.Size()); + + // Check to see that the first string, if repeated, is discarded. + EXPECT_EQ(DnsQueue::REDUNDANT_PUSH, buffer.Push(input_string)); + EXPECT_EQ(1, buffer.Size()); +} + +TEST(DnsQueueTest, FillThenEmptyCheck) { + // Use a big buffer so we'll get a bunch of writes in. + // This tests to be sure the buffer holds many strings. + // We also make sure they all come out intact. + const int buffer_size = 1000; + int byte_usage_counter = 1; // Separation character between pointer. + DnsQueue buffer(buffer_size); + DnsQueueSequentialTester tester(buffer); + + int write_success; + for (write_success = 0; write_success < buffer_size; write_success++) { + if (!tester.Push()) + break; + EXPECT_EQ(buffer.Size(), write_success + 1); + if (write_success > 99) + byte_usage_counter += 4; // 3 digit plus '\0'. + else if (write_success > 9) + byte_usage_counter += 3; // 2 digits plus '\0'. + else + byte_usage_counter += 2; // Digit plus '\0'. + } + EXPECT_LE(byte_usage_counter, buffer_size) + << "Written data exceeded buffer size"; + EXPECT_GE(byte_usage_counter, buffer_size - 4) + << "Buffer does not appear to have filled"; + + EXPECT_GE(write_success, 10) << "Couldn't even write 10 one digit strings " + "in " << buffer_size << " byte buffer"; + + + while (1) { + if (!tester.Pop()) + break; + write_success--; + } + EXPECT_EQ(write_success, 0) << "Push and Pop count were different"; + + EXPECT_FALSE(tester.Pop()) << "Read from empty buffer succeeded"; +} + +TEST(DnsQueueTest, ClearCheck) { + // Use a big buffer so we'll get a bunch of writes in. + const int buffer_size = 1000; + DnsQueue buffer(buffer_size); + std::string string("ABC"); + DnsQueueSequentialTester tester(buffer); + + int write_success; + for (write_success = 0; write_success < buffer_size; write_success++) { + if (!tester.Push()) + break; + EXPECT_EQ(buffer.Size(), write_success + 1); + } + + buffer.Clear(); + EXPECT_EQ(buffer.Size(), 0); + + int write_success2; + for (write_success2 = 0; write_success2 < buffer_size; write_success2++) { + if (!tester.Push()) + break; + EXPECT_EQ(buffer.Size(), write_success2 + 1); + } + + for (; write_success2 > 0; write_success2--) { + EXPECT_EQ(buffer.Size(), write_success2); + EXPECT_TRUE(buffer.Pop(&string)); + } + + EXPECT_EQ(buffer.Size(), 0); + buffer.Clear(); + EXPECT_EQ(buffer.Size(), 0); +} + +TEST(DnsQueueTest, WrapOnVariousSubstrings) { + // Use a prime number for the allocated buffer size so that we tend + // to exercise all possible edge conditions (in circular text buffer). + // Once we're over 10 writes, all our strings are 2 digits long, + // with a '\0' terminator added making 3 characters per write. + // Since 3 is relatively prime to 23, we'll soon wrap (about + // every 6 writes). Hence after 18 writes, we'll have tested all + // edge conditions. We'll first do this where we empty the buffer + // after each write, and then again where there are some strings + // still in the buffer after each write. + const int prime_number = 23; + // Circular buffer needs an extra extra space to distinguish full from empty. + const int buffer_size = prime_number - 1; + DnsQueue buffer(buffer_size); + DnsQueueSequentialTester tester(buffer); + + // First test empties between each write. Second loop + // has writes for each pop. Third has three pushes per pop. + // Third has two items pending during each write. + for (int j = 0; j < 3; j++) { + // Each group does 30 tests, which is more than 10+18 + // which was needed to get into the thorough testing zone + // mentioned above. + for (int i = 0; i < 30; i++) { + EXPECT_TRUE(tester.Push()) << "write failed with only " << j + << " blocks in buffer"; + EXPECT_TRUE(tester.Pop()) << "Unable to read back data "; + } + EXPECT_TRUE(tester.Push()); + } + + // Read back the accumulated 3 extra blocks. + EXPECT_TRUE(tester.Pop()); + EXPECT_TRUE(tester.Pop()); + EXPECT_TRUE(tester.Pop()); + EXPECT_FALSE(tester.Pop()); +} + +}; // namespace diff --git a/chrome/renderer/plugin_channel_host.cc b/chrome/renderer/plugin_channel_host.cc new file mode 100644 index 0000000..a31ee83 --- /dev/null +++ b/chrome/renderer/plugin_channel_host.cc @@ -0,0 +1,135 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/renderer/plugin_channel_host.h" + +#include "chrome/common/plugin_messages.h" + +// A simple MessageFilter that will ignore all messages and respond to sync +// messages with an error when is_listening_ is false. +class IsListeningFilter : public IPC::ChannelProxy::MessageFilter { + public: + IsListeningFilter() {} + + // MessageFilter overrides + virtual void OnFilterRemoved() {} + virtual void OnFilterAdded(IPC::Channel* channel) { channel_ = channel; } + virtual bool OnMessageReceived(const IPC::Message& message); + + static bool is_listening_; + + private: + IPC::Channel* channel_; + + DISALLOW_EVIL_CONSTRUCTORS(IsListeningFilter); +}; + +bool IsListeningFilter::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + if (!IsListeningFilter::is_listening_) { + // reply to synchronous messages with an error (so they don't block while + // we're not listening) + if (message.is_sync()) { + IPC::Message* reply = IPC::SyncMessage::GenerateReply(&message); + reply->set_reply_error(); + channel_->Send(reply); + } + handled = true; + } else { + handled = false; + } + return handled; +} + +// static +bool IsListeningFilter::is_listening_ = true; + +// static +void PluginChannelHost::SetListening(bool flag) { + IsListeningFilter::is_listening_ = flag; +} + +PluginChannelHost* PluginChannelHost::GetPluginChannelHost( + const std::wstring& channel_name, MessageLoop* ipc_message_loop) { + PluginChannelHost* result = + static_cast<PluginChannelHost*>(PluginChannelBase::GetChannel( + channel_name, + IPC::Channel::MODE_CLIENT, + ClassFactory, + ipc_message_loop, + true)); + return result; +} + +PluginChannelHost::PluginChannelHost() { +} + +PluginChannelHost::~PluginChannelHost() { +} + +bool PluginChannelHost::Init(MessageLoop* ipc_message_loop, + bool create_pipe_now) { + bool ret = PluginChannelBase::Init(ipc_message_loop, create_pipe_now); + is_listening_filter_ = new IsListeningFilter; + channel_->AddFilter(is_listening_filter_); + return ret; +} + +int PluginChannelHost::GenerateRouteID() { + int route_id = MSG_ROUTING_NONE; + Send(new PluginMsg_GenerateRouteID(&route_id)); + + return route_id; +} + +void PluginChannelHost::AddRoute(int route_id, + IPC::Channel::Listener* listener, + bool npobject) { + PluginChannelBase::AddRoute(route_id, listener, npobject); + + if (!npobject) + proxies_[route_id] = listener; +} + +void PluginChannelHost::RemoveRoute(int route_id) { + proxies_.erase(route_id); + PluginChannelBase::RemoveRoute(route_id); +} + +void PluginChannelHost::OnChannelError() { + PluginChannelBase::OnChannelError(); + + for (ProxyMap::iterator iter = proxies_.begin(); + iter != proxies_.end(); iter++) { + iter->second->OnChannelError(); + } + + proxies_.clear(); +} + diff --git a/chrome/renderer/plugin_channel_host.h b/chrome/renderer/plugin_channel_host.h new file mode 100644 index 0000000..aed6f79 --- /dev/null +++ b/chrome/renderer/plugin_channel_host.h @@ -0,0 +1,76 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_PLUGIN_PLUGIN_CHANNEL_HOST_H__ +#define CHROME_PLUGIN_PLUGIN_CHANNEL_HOST_H__ + +#include "chrome/plugin/plugin_channel_base.h" + +class IsListeningFilter; + +// Encapsulates an IPC channel between the renderer and one plugin process. +// On the plugin side there's a corresponding PluginChannel. +class PluginChannelHost : public PluginChannelBase { + public: + static PluginChannelHost* GetPluginChannelHost( + const std::wstring& channel_name, MessageLoop* ipc_message_loop); + + ~PluginChannelHost(); + + virtual bool Init(MessageLoop* ipc_message_loop, bool create_pipe_now); + + int GenerateRouteID(); + + void AddRoute(int route_id, IPC::Channel::Listener* listener, bool npobject); + void RemoveRoute(int route_id); + + // IPC::Channel::Listener override + void OnChannelError(); + + static void SetListening(bool flag); + + private: + // Called on the render thread + PluginChannelHost(); + + static PluginChannelBase* ClassFactory() { return new PluginChannelHost(); } + + // Keep track of all the registered WebPluginDelegeProxies to + // inform about OnChannelError + typedef stdext::hash_map<int, IPC::Channel::Listener*> ProxyMap; + ProxyMap proxies_; + + // An IPC MessageFilter that can be told to filter out all messages. This is + // used when the JS debugger is attached in order to avoid browser hangs. + scoped_refptr<IsListeningFilter> is_listening_filter_; + + DISALLOW_EVIL_CONSTRUCTORS(PluginChannelHost); +}; + +#endif // CHROME_PLUGIN_PLUGIN_CHANNEL_HOST_H__ diff --git a/chrome/renderer/render_process.cc b/chrome/renderer/render_process.cc new file mode 100644 index 0000000..5d15ba4 --- /dev/null +++ b/chrome/renderer/render_process.cc @@ -0,0 +1,273 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> +#include <objidl.h> +#include <mlang.h> + +#include "chrome/renderer/render_process.h" + +#include "base/basictypes.h" +#include "base/command_line.h" +#include "base/message_loop.h" +#include "base/histogram.h" +#include "chrome/browser/net/dns_global.h" // TODO(jar): DNS calls should be renderer specific, not including browser. +#include "chrome/common/chrome_switches.h" +#include "chrome/common/ipc_channel.h" +#include "chrome/common/ipc_message_utils.h" +#include "chrome/common/render_messages.h" +#include "chrome/renderer/render_view.h" +#include "webkit/glue/webkit_glue.h" + +//----------------------------------------------------------------------------- + +IMLangFontLink2* RenderProcess::lang_font_link_ = NULL; +bool RenderProcess::load_plugins_in_process_ = false; + +//----------------------------------------------------------------------------- + +RenderProcess::RenderProcess(const std::wstring& channel_name) + : render_thread_(channel_name), +#pragma warning(suppress: 4355) // Okay to pass "this" here. + clearer_factory_(this) { + for (int i = 0; i < arraysize(shared_mem_cache_); ++i) + shared_mem_cache_[i] = NULL; +} + +RenderProcess::~RenderProcess() { + // We need to stop the RenderThread as the clearer_factory_ + // member could be in use while the object itself is destroyed, + // as a result of the containing RenderProcess object being destroyed. + // This race condition causes a crash when the renderer process is shutting + // down. + render_thread_.Stop(); + ClearSharedMemCache(); +} + +// static +bool RenderProcess::GlobalInit(const std::wstring &channel_name) { + // HACK: See http://b/issue?id=1024307 for rationale. + if (GetModuleHandle(L"LPK.DLL") == NULL) { + // Makes sure lpk.dll is loaded by gdi32 to make sure ExtTextOut() works + // when buffering into a EMF buffer for printing. + typedef BOOL (__stdcall *GdiInitializeLanguagePack)(int LoadedShapingDLLs); + GdiInitializeLanguagePack gdi_init_lpk = + reinterpret_cast<GdiInitializeLanguagePack>(GetProcAddress( + GetModuleHandle(L"GDI32.DLL"), + "GdiInitializeLanguagePack")); + DCHECK(gdi_init_lpk); + if (gdi_init_lpk) { + gdi_init_lpk(0); + } + } + + InitializeLangFontLink(); + + CommandLine command_line; + if (command_line.HasSwitch(switches::kJavaScriptFlags)) { + webkit_glue::SetJavaScriptFlags( + command_line.GetSwitchValue(switches::kJavaScriptFlags)); + } + if (command_line.HasSwitch(switches::kPlaybackMode) || + command_line.HasSwitch(switches::kRecordMode)) { + webkit_glue::SetRecordPlaybackMode(true); + } + + if (command_line.HasSwitch(switches::kInProcessPlugins) || + command_line.HasSwitch(switches::kSingleProcess)) + load_plugins_in_process_ = true; + + if (command_line.HasSwitch(switches::kDnsPrefetchDisable)) { + chrome_browser_net::EnableDnsPrefetch(false); + } + + if (command_line.HasSwitch(switches::kEnableWatchdog)) { + // TODO(JAR): Need to implement renderer IO msgloop watchdog. + } + + if (command_line.HasSwitch(switches::kDumpHistogramsOnExit)) { + StatisticsRecorder::set_dump_on_exit(true); + } + + ChildProcessFactory<RenderProcess> factory; + return ChildProcess::GlobalInit(channel_name, &factory); +} + +// static +void RenderProcess::GlobalCleanup() { + ChildProcess::GlobalCleanup(); + ReleaseLangFontLink(); +} + +// static +void RenderProcess::InitializeLangFontLink() { + // TODO(hbono): http://b/1072298 Experimentally commented out this code to + // prevent registry leaks caused by this IMLangFontLink2 interface. + // If you find any font-rendering regressions. Please feel free to blame me. +#ifdef USE_IMLANGFONTLINK2 + IMultiLanguage* multi_language = NULL; + lang_font_link_ = NULL; + if (S_OK != CoCreateInstance(CLSID_CMultiLanguage, + 0, + CLSCTX_ALL, + IID_IMultiLanguage, + reinterpret_cast<void**>(&multi_language))) { + DLOG(ERROR) << "Cannot CoCreate CMultiLanguage"; + } else { + if (S_OK != multi_language->QueryInterface(IID_IMLangFontLink2, + reinterpret_cast<void**>(&lang_font_link_))) { + DLOG(ERROR) << "Cannot query LangFontLink2 interface"; + } + } + + if (multi_language) + multi_language->Release(); +#endif +} + +// static +void RenderProcess::ReleaseLangFontLink() { + // TODO(hbono): http://b/1072298 Experimentally commented out this code to + // prevent registry leaks caused by this IMLangFontLink2 interface. + // If you find any font-rendering regressions. Please feel free to blame me. +#ifdef USE_IMLANGFONTLINK2 + if (lang_font_link_) + lang_font_link_->Release(); +#endif +} + +// static +IMLangFontLink2* RenderProcess::GetLangFontLink() { + return lang_font_link_; +} + +// static +bool RenderProcess::ShouldLoadPluginsInProcess() { + return load_plugins_in_process_; +} + +// static +SharedMemory* RenderProcess::AllocSharedMemory(size_t size) { + self()->clearer_factory_.RevokeAll(); + + SharedMemory* mem = self()->GetSharedMemFromCache(size); + if (mem) + return mem; + + // Round-up size to allocation granularity + SYSTEM_INFO info; + GetSystemInfo(&info); + + size = size / info.dwAllocationGranularity + 1; + size = size * info.dwAllocationGranularity; + + mem = new SharedMemory(); + if (!mem) + return NULL; + if (!mem->Create(L"", false, true, size)) { + delete mem; + return NULL; + } + + return mem; +} + +// static +void RenderProcess::FreeSharedMemory(SharedMemory* mem) { + if (self()->PutSharedMemInCache(mem)) { + self()->ScheduleCacheClearer(); + return; + } + DeleteSharedMem(mem); +} + +// static +void RenderProcess::DeleteSharedMem(SharedMemory* mem) { + delete mem; +} + +SharedMemory* RenderProcess::GetSharedMemFromCache(size_t size) { + // look for a cached object that is suitable for the requested size. + for (int i = 0; i < arraysize(shared_mem_cache_); ++i) { + SharedMemory* mem = shared_mem_cache_[i]; + if (mem && mem->max_size() >= size) { + shared_mem_cache_[i] = NULL; + return mem; + } + } + return NULL; +} + +bool RenderProcess::PutSharedMemInCache(SharedMemory* mem) { + // simple algorithm: + // - look for an empty slot to store mem, or + // - if full, then replace any existing cache entry that is smaller than the + // given shared memory object. + for (int i = 0; i < arraysize(shared_mem_cache_); ++i) { + if (!shared_mem_cache_[i]) { + shared_mem_cache_[i] = mem; + return true; + } + } + for (int i = 0; i < arraysize(shared_mem_cache_); ++i) { + SharedMemory* cached_mem = shared_mem_cache_[i]; + if (cached_mem->max_size() < mem->max_size()) { + shared_mem_cache_[i] = mem; + DeleteSharedMem(cached_mem); + return true; + } + } + return false; +} + +void RenderProcess::ClearSharedMemCache() { + for (int i = 0; i < arraysize(shared_mem_cache_); ++i) { + if (shared_mem_cache_[i]) { + DeleteSharedMem(shared_mem_cache_[i]); + shared_mem_cache_[i] = NULL; + } + } +} + +void RenderProcess::ScheduleCacheClearer() { + // If we already have a deferred clearer, then revoke it so we effectively + // delay cache clearing until idle for our desired interval. + clearer_factory_.RevokeAll(); + + MessageLoop::current()->PostDelayedTask(FROM_HERE, + clearer_factory_.NewRunnableMethod(&RenderProcess::ClearSharedMemCache), + 5000 /* 5 seconds */); +} + +void RenderProcess::Cleanup() { +#ifndef NDEBUG + // log important leaked objects + webkit_glue::CheckForLeaks(); +#endif +} diff --git a/chrome/renderer/render_process.h b/chrome/renderer/render_process.h new file mode 100644 index 0000000..49f43e2 --- /dev/null +++ b/chrome/renderer/render_process.h @@ -0,0 +1,126 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_RENDERER_RENDER_PROCESS_H__ +#define CHROME_RENDERER_RENDER_PROCESS_H__ + +#include <objidl.h> +#include <mlang.h> + +#include "base/shared_memory.h" +#include "chrome/common/child_process.h" +#include "chrome/renderer/render_thread.h" + +class RenderView; + +// Represents the renderer end of the browser<->renderer connection. The +// opposite end is the RenderProcessHost. This is a singleton object for +// each renderer. +class RenderProcess : public ChildProcess { + public: + static bool GlobalInit(const std::wstring& channel_name); + static void GlobalCleanup(); + + // Returns true if plugins should be loaded in-process. + static bool ShouldLoadPluginsInProcess(); + + static IMLangFontLink2* GetLangFontLink(); + + // Allocates shared memory. When no longer needed, you should pass the + // SharedMemory pointer to FreeSharedMemory so it can be recycled. The size + // reported in the resulting SharedMemory object will be greater than or + // equal to the requested size. This method returns NULL if unable to + // allocate memory for some reason. + static SharedMemory* AllocSharedMemory(size_t size); + + // Frees shared memory allocated by AllocSharedMemory. You should only use + // this function to free the SharedMemory object. + static void FreeSharedMemory(SharedMemory* mem); + + private: + friend class ChildProcessFactory<RenderProcess>; + RenderProcess(const std::wstring& channel_name); + ~RenderProcess(); + + // Initializes the LangFontLink object. This function cannot be called + // while the process is restricted by the sandbox or it will fail. + static void InitializeLangFontLink(); + + // Releases the LangFontLink object if already created. + static void ReleaseLangFontLink(); + + // Returns a pointer to the RenderProcess singleton instance. This is + // guaranteed to be non-NULL between calls to GlobalInit and GlobalCleanup. + static RenderProcess* self() { + return static_cast<RenderProcess*>(child_process_); + } + + static ChildProcess* ClassFactory(const std::wstring& channel_name); + + // This is here so consumers will use FreeSharedMemory instead. A destructor + // on SharedMemory would be too tempting. + static void DeleteSharedMem(SharedMemory* mem); + + // Look in the shared memory cache for a suitable object to reuse. Returns + // NULL if there is none. + SharedMemory* GetSharedMemFromCache(size_t size); + + // Maybe put the given shared memory into the shared memory cache. Returns + // true if the SharedMemory object was stored in the cache; otherwise, false + // is returned. + bool PutSharedMemInCache(SharedMemory* mem); + + void ClearSharedMemCache(); + + // We want to lazily clear the shared memory cache if no one has requested + // memory. This methods are used to schedule a deferred call to + // RenderProcess::ClearSharedMemCache. + void ScheduleCacheClearer(); + + // ChildProcess implementation + virtual void Cleanup(); + + // The one render thread (to be replaced with a set of render threads). + RenderThread render_thread_; + + // A very simplistic and small cache. If an entry in this array is non-null, + // then it points to a SharedMemory object that is available for reuse. + SharedMemory* shared_mem_cache_[2]; + + // This factory is used to lazily invoke ClearSharedMemCache. + ScopedRunnableMethodFactory<RenderProcess> clearer_factory_; + + static IMLangFontLink2* lang_font_link_; + + static bool load_plugins_in_process_; + + DISALLOW_EVIL_CONSTRUCTORS(RenderProcess); +}; + +#endif // CHROME_RENDERER_RENDER_PROCESS_H__ diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc new file mode 100644 index 0000000..4288377 --- /dev/null +++ b/chrome/renderer/render_thread.cc @@ -0,0 +1,220 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> +#include <algorithm> + +#include "chrome/renderer/render_thread.h" + +#include "base/shared_memory.h" +#include "chrome/common/ipc_logging.h" +#include "chrome/plugin/plugin_channel.h" +#include "chrome/renderer/net/render_dns_master.h" +#include "chrome/renderer/render_process.h" +#include "chrome/renderer/render_view.h" +#include "chrome/renderer/visitedlink_slave.h" +#include "webkit/glue/cache_manager.h" + +static const unsigned int kCacheStatsDelayMS = 2000 /* milliseconds */; + +// V8 needs a 1MB stack size. +static const size_t kStackSize = 1024 * 1024; + +/*static*/ +DWORD RenderThread::tls_index_ = ThreadLocalStorage::Alloc(); + +//----------------------------------------------------------------------------- +// Methods below are only called on the owner's thread: + +RenderThread::RenderThread(const std::wstring& channel_name) + : Thread("Chrome_RenderThread"), + channel_name_(channel_name), + owner_loop_(MessageLoop::current()), + visited_link_slave_(NULL), + render_dns_master_(NULL), + in_send_(0) { + DCHECK(owner_loop_); + StartWithStackSize(kStackSize); +} + +RenderThread::~RenderThread() { + Stop(); +} + +void RenderThread::OnChannelError() { + // XXX(darin): is this really correct/sufficient? + owner_loop_->Quit(); +} + +bool RenderThread::Send(IPC::Message* msg) { + in_send_++; + bool rv = channel_->Send(msg); + in_send_--; + return rv; +} + +void RenderThread::AddFilter(IPC::ChannelProxy::MessageFilter* filter) { + channel_->AddFilter(filter); +} + +void RenderThread::RemoveFilter(IPC::ChannelProxy::MessageFilter* filter) { + channel_->RemoveFilter(filter); +} + +void RenderThread::Resolve(const char* name, size_t length) { + return render_dns_master_->Resolve(name, length); + } + +void RenderThread::AddRoute(int32 routing_id, + IPC::Channel::Listener* listener) { + DCHECK(MessageLoop::current() == message_loop()); + + // This corresponds to the AddRoute call done in CreateView. + router_.AddRoute(routing_id, listener); +} + +void RenderThread::RemoveRoute(int32 routing_id) { + DCHECK(MessageLoop::current() == message_loop()); + + router_.RemoveRoute(routing_id); +} + +void RenderThread::Init() { + DCHECK(tls_index_) << "static initializer failed"; + DCHECK(!current()) << "should only have one RenderThread per thread"; + + cache_stats_factory_.reset( + new ScopedRunnableMethodFactory<RenderThread>(this)); + + channel_.reset(new IPC::SyncChannel(channel_name_, + IPC::Channel::MODE_CLIENT, this, owner_loop_, true)); + + ThreadLocalStorage::Set(tls_index_, this); + + // The renderer thread should wind-up COM. + CoInitialize(0); + + // TODO(darin): We should actually try to share this object between + // RenderThread instances. + visited_link_slave_ = new VisitedLinkSlave(); + + render_dns_master_.reset(new RenderDnsMaster()); + +#ifdef IPC_MESSAGE_LOG_ENABLED + IPC::Logging::current()->SetIPCSender(this); +#endif +} + +void RenderThread::CleanUp() { + DCHECK(current() == this); + + // Clean up plugin channels before this thread goes away. + PluginChannelBase::CleanupChannels(); + +#ifdef IPC_MESSAGE_LOG_ENABLED + IPC::Logging::current()->SetIPCSender(NULL); +#endif + + delete visited_link_slave_; + visited_link_slave_ = NULL; + + CoUninitialize(); +} + +void RenderThread::OnUpdateVisitedLinks(SharedMemoryHandle table) { + DCHECK(table) << "Bad table handle"; + visited_link_slave_->Init(table); +} + +void RenderThread::OnMessageReceived(const IPC::Message& msg) { + // NOTE: We could subclass router_ to intercept OnControlMessageReceived, but + // it seems simpler to just process any control messages that we care about + // up-front and then send the rest of the messages onto router_. + + if (msg.routing_id() == MSG_ROUTING_CONTROL) { + IPC_BEGIN_MESSAGE_MAP(RenderThread, msg) + IPC_MESSAGE_HANDLER(ViewMsg_VisitedLink_NewTable, OnUpdateVisitedLinks) + IPC_MESSAGE_HANDLER(ViewMsg_SetNextPageID, OnSetNextPageID) + IPC_MESSAGE_HANDLER(ViewMsg_New, OnCreateNewView) + IPC_MESSAGE_HANDLER(ViewMsg_SetCacheCapacities, OnSetCacheCapacities) + IPC_MESSAGE_HANDLER(ViewMsg_GetCacheResourceStats, + OnGetCacheResourceStats) + // send the rest to the router + IPC_MESSAGE_UNHANDLED(router_.OnMessageReceived(msg)) + IPC_END_MESSAGE_MAP() + } else { + router_.OnMessageReceived(msg); + } +} + +void RenderThread::OnSetNextPageID(int32 next_page_id) { + // This should only be called at process initialization time, so we shouldn't + // have to worry about thread-safety. + RenderView::SetNextPageID(next_page_id); +} + +void RenderThread::OnCreateNewView(HWND parent_hwnd, + HANDLE modal_dialog_event, + const WebPreferences& webkit_prefs, + int32 view_id) { + // TODO(darin): once we have a RenderThread per RenderView, this will need to + // change to assert that we are not creating more than one view. + + RenderView::Create( + parent_hwnd, modal_dialog_event, MSG_ROUTING_NONE, webkit_prefs, view_id); +} + +void RenderThread::OnSetCacheCapacities(size_t min_dead_capacity, + size_t max_dead_capacity, + size_t capacity) { + CacheManager::SetCapacities(min_dead_capacity, max_dead_capacity, capacity); +} + +void RenderThread::OnGetCacheResourceStats() { + CacheManager::ResourceTypeStats stats; + CacheManager::GetResourceTypeStats(&stats); + Send(new ViewHostMsg_ResourceTypeStats(stats)); +} + +void RenderThread::InformHostOfCacheStats() { + CacheManager::UsageStats stats; + CacheManager::GetUsageStats(&stats); + Send(new ViewHostMsg_UpdatedCacheStats(stats)); +} + +void RenderThread::InformHostOfCacheStatsLater() { + // Rate limit informing the host of our cache stats. + if (!cache_stats_factory_->empty()) + return; + + MessageLoop::current()->PostDelayedTask(FROM_HERE, + cache_stats_factory_->NewRunnableMethod( + &RenderThread::InformHostOfCacheStats), + kCacheStatsDelayMS); +} diff --git a/chrome/renderer/render_thread.h b/chrome/renderer/render_thread.h new file mode 100644 index 0000000..ca022671 --- /dev/null +++ b/chrome/renderer/render_thread.h @@ -0,0 +1,145 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_RENDERER_RENDER_THREAD_H__ +#define CHROME_RENDERER_RENDER_THREAD_H__ + +#include "base/ref_counted.h" +#include "base/shared_memory.h" +#include "base/task.h" +#include "base/thread.h" +#include "base/thread_local_storage.h" +#include "chrome/common/ipc_sync_channel.h" +#include "chrome/common/message_router.h" + +class SkBitmap; +class Task; +class VisitedLinkSlave; +struct WebPreferences; +class RenderDnsMaster; + +// The RenderThread class represents a background thread where RenderView +// instances live. The RenderThread supports an API that is used by its +// consumer to talk indirectly to the RenderViews and supporting objects. +// Likewise, it provides an API for the RenderViews to talk back to the main +// process (i.e., their corresponding WebContents). +// +// Most of the communication occurs in the form of IPC messages. They are +// routed to the RenderThread according to the routing IDs of the messages. +// The routing IDs correspond to RenderView instances. + +class RenderThread : public IPC::Channel::Listener, + public IPC::Message::Sender, + public Thread { + public: + RenderThread(const std::wstring& channel_name); + ~RenderThread(); + + // IPC::Channel::Listener implementation: + virtual void OnMessageReceived(const IPC::Message& msg); + virtual void OnChannelError(); + + // IPC::Message::Sender implementation: + virtual bool Send(IPC::Message* msg); + + void AddFilter(IPC::ChannelProxy::MessageFilter* filter); + void RemoveFilter(IPC::ChannelProxy::MessageFilter* filter); + + // The RenderThread instance for the current thread. + static RenderThread* current() { + return static_cast<RenderThread*>(ThreadLocalStorage::Get(tls_index_)); + } + + VisitedLinkSlave* visited_link_slave() const { return visited_link_slave_; } + + // Do DNS prefetch resolution of a hostname. + void Resolve(const char* name, size_t length); + + // See documentation on MessageRouter for AddRoute and RemoveRoute + void AddRoute(int32 routing_id, IPC::Channel::Listener* listener); + void RemoveRoute(int32 routing_id); + + // Invokes InformHostOfCacheStats after a short delay. Used to move this + // bookkeeping operation off the critical latency path. + void InformHostOfCacheStatsLater(); + + MessageLoop* owner_loop() { return owner_loop_; } + + // Indicates if RenderThread::Send() is on the call stack. + bool in_send() const { return in_send_ != 0; } + + protected: + // Called by the thread base class + virtual void Init(); + virtual void CleanUp(); + + private: + void OnUpdateVisitedLinks(SharedMemoryHandle table); + + void OnSetNextPageID(int32 next_page_id); + void OnCreateNewView(HWND parent_hwnd, + HANDLE modal_dialog_event, + const WebPreferences& webkit_prefs, + int32 view_id); + void OnTransferBitmap(const SkBitmap& bitmap, int resource_id); + void OnSetCacheCapacities(size_t min_dead_capacity, + size_t max_dead_capacity, + size_t capacity); + void OnGetCacheResourceStats(); + + // Gather usage statistics from the in-memory cache and inform our host. + // These functions should be call periodically so that the host can make + // decisions about how to allocation resources using current information. + void InformHostOfCacheStats(); + + static DWORD tls_index_; + + // The message loop used to run tasks on the thread that started this thread. + MessageLoop* owner_loop_; + + // Used only on the background render thread to implement message routing + // functionality to the consumers of the RenderThread. + MessageRouter router_; + + std::wstring channel_name_; + scoped_ptr<IPC::SyncChannel> channel_; + + // These objects live solely on the render thread. + VisitedLinkSlave* visited_link_slave_; + + scoped_ptr<RenderDnsMaster> render_dns_master_; + + scoped_ptr<ScopedRunnableMethodFactory<RenderThread> > cache_stats_factory_; + + int in_send_; + + DISALLOW_EVIL_CONSTRUCTORS(RenderThread); +}; + +#endif // CHROME_RENDERER_RENDER_THREAD_H__ diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc new file mode 100644 index 0000000..519e37d --- /dev/null +++ b/chrome/renderer/render_view.cc @@ -0,0 +1,2469 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/renderer/render_view.h" + +#include <algorithm> +#include <string> +#include <vector> + +#include "base/command_line.h" +#include "base/gfx/bitmap_header.h" +#include "base/gfx/bitmap_platform_device.h" +#include "base/gfx/image_operations.h" +#include "base/gfx/native_theme.h" +#include "base/gfx/vector_canvas.h" +#include "base/gfx/png_encoder.h" +#include "base/string_piece.h" +#include "base/string_util.h" +#include "chrome/app/theme/theme_resources.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/gfx/emf.h" +#include "chrome/common/gfx/favicon_size.h" +#include "chrome/common/gfx/color_utils.h" +#include "chrome/common/jstemplate_builder.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/common/text_zoom.h" +#include "chrome/common/thumbnail_score.h" +#include "chrome/renderer/about_handler.h" +#include "chrome/renderer/debug_message_handler.h" +#include "chrome/renderer/localized_error.h" +#include "chrome/renderer/renderer_resources.h" +#include "chrome/renderer/visitedlink_slave.h" +#include "chrome/renderer/webplugin_delegate_proxy.h" +#include "chrome/views/message_box_view.h" +#include "net/base/escape.h" +#include "net/base/net_errors.h" +#include "webkit/default_plugin/default_plugin_shared.h" +#include "webkit/glue/dom_operations.h" +#include "webkit/glue/dom_serializer.h" +#include "webkit/glue/password_form.h" +#include "webkit/glue/plugins/plugin_list.h" +#include "webkit/glue/searchable_form_data.h" +#include "webkit/glue/webdatasource.h" +#include "webkit/glue/webdropdata.h" +#include "webkit/glue/weberror.h" +#include "webkit/glue/webframe.h" +#include "webkit/glue/webhistoryitem.h" +#include "webkit/glue/webinputevent.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/webpreferences.h" +#include "webkit/glue/webresponse.h" +#include "webkit/glue/weburlrequest.h" +#include "webkit/glue/webview.h" +#include "webkit/glue/plugins/webplugin_delegate_impl.h" +#include "webkit/port/platform/graphics/PlatformContextSkia.h" + +#include "generated_resources.h" + +//----------------------------------------------------------------------------- + +// define to write the time necessary for thumbnail/DOM text retrieval, +// respectively, into the system debug log +// #define TIME_BITMAP_RETRIEVAL +// #define TIME_TEXT_RETRIEVAL + +// maximum number of characters in the document to index, any text beyond this +// point will be clipped +static const int kMaxIndexChars = 65535; + +// Size of the thumbnails that we'll generate +static const int kThumbnailWidth = 196; +static const int kThumbnailHeight = 136; + +// Delay in milliseconds that we'll wait before capturing the page contents +// and thumbnail. +static const int kDelayForCaptureMs = 500; + +// Typically, we capture the page data once the page is loaded. +// Sometimes, the page never finishes to load, preventing the page capture +// To workaround this problem, we always perform a capture after the following +// delay. +static const int kDelayForForcedCaptureMs = 6000; + +// How often we will sync the navigation state when the user is changing form +// elements or scroll position. +const TimeDelta kDelayForNavigationSync = TimeDelta::FromSeconds(5); + +// The next available page ID to use. This ensures that the page IDs are +// globally unique in the renderer. +static int32 next_page_id_ = 1; + +static const char* const kUnreachableWebDataURL = + "chrome-resource://chromewebdata/"; + +namespace { + +// Associated with browser-initiated navigations to hold tracking data. +class RenderViewExtraRequestData : public WebRequest::ExtraData { + public: + RenderViewExtraRequestData(int32 pending_page_id, + PageTransition::Type transition, + const GURL& url) + : pending_page_id_(pending_page_id), + transition_type(transition), + request_committed(false) { + } + + // Contains the page_id for this navigation or -1 if there is none yet. + int32 pending_page_id() const { return pending_page_id_; } + + // Is this a new navigation? + bool is_new_navigation() const { return pending_page_id_ == -1; } + + // Contains the transition type that the browser specified when it + // initiated the load. + PageTransition::Type transition_type; + + // True if we have already processed the "DidCommitLoad" event for this + // request. Used by session history. + bool request_committed; + + private: + int32 pending_page_id_; + + DISALLOW_EVIL_CONSTRUCTORS(RenderViewExtraRequestData); +}; + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// + +RenderView::RenderView() + : RenderWidget(), + is_loading_(false), + page_id_(-1), + last_page_id_sent_to_browser_(-1), + last_indexed_page_id_(-1), + method_factory_(this), + nav_state_sync_timer_(kDelayForNavigationSync), + opened_by_user_gesture_(true), + enable_dom_automation_(false), + enable_dom_ui_bindings_(false), + target_url_status_(TARGET_NONE), + printed_document_width_(0), + first_default_plugin_(NULL), + navigation_gesture_(NavigationGestureUnknown), + history_back_list_count_(0), + history_forward_list_count_(0), + disable_popup_blocking_(false), + has_unload_listener_(false) { + resource_dispatcher_ = new ResourceDispatcher(this); + nav_state_sync_timer_.set_task( + method_factory_.NewRunnableMethod(&RenderView::SyncNavigationState)); +} + +RenderView::~RenderView() { + resource_dispatcher_->ClearMessageSender(); + // Clear any back-pointers that might still be held by plugins. + PluginDelegateList::iterator it = plugin_delegates_.begin(); + while (it != plugin_delegates_.end()) { + (*it)->DropRenderView(); + it = plugin_delegates_.erase(it); + } + + RenderThread::current()->RemoveFilter(debug_message_handler_); +} + +/*static*/ +RenderView* RenderView::Create(HWND parent_hwnd, + HANDLE modal_dialog_event, + int32 opener_id, + const WebPreferences& webkit_prefs, + int32 routing_id) { + DCHECK(routing_id != MSG_ROUTING_NONE); + scoped_refptr<RenderView> view = new RenderView(); + view->Init(parent_hwnd, + modal_dialog_event, + opener_id, + webkit_prefs, + routing_id); // adds reference + return view; +} + +/*static*/ +void RenderView::SetNextPageID(int32 next_page_id) { + // This method should only be called during process startup, and the given + // page id had better not exceed our current next page id! + DCHECK(next_page_id_ == 1); + DCHECK(next_page_id >= next_page_id_); + next_page_id_ = next_page_id; +} + +void RenderView::PluginDestroyed(WebPluginDelegateProxy* proxy) { + PluginDelegateList::iterator it = + std::find(plugin_delegates_.begin(), plugin_delegates_.end(), proxy); + DCHECK(it != plugin_delegates_.end()); + plugin_delegates_.erase(it); + // If the plugin is deleted, we need to clear our reference in case user + // clicks the info bar to install. Unfortunately we are getting + // PluginDestroyed in single process mode. However, that is not a huge + // concern. + if (proxy == first_default_plugin_) + first_default_plugin_ = NULL; +} + +void RenderView::PluginCrashed(const std::wstring& plugin_path) { + Send(new ViewHostMsg_CrashedPlugin(routing_id_, plugin_path)); +} + + +void RenderView::JSOutOfMemory() { + Send(new ViewHostMsg_JSOutOfMemory(routing_id_)); +} + +void RenderView::Init(HWND parent_hwnd, + HANDLE modal_dialog_event, + int32 opener_id, + const WebPreferences& webkit_prefs, + int32 routing_id) { + DCHECK(!webview()); + + if (opener_id != MSG_ROUTING_NONE) + opener_id_ = opener_id; + + // Avoid a leak here by not assigning, since WebView::Create addrefs for us. + WebWidget* view = WebView::Create(this, webkit_prefs); + webwidget_.swap(&view); + + // Don't let WebCore keep a B/F list - we have our own. + // We let it keep 1 entry because FrameLoader::goToItem expects an item in the + // backForwardList, which is used only in ASSERTs. + webview()->SetBackForwardListSize(1); + + routing_id_ = routing_id; + RenderThread::current()->AddRoute(routing_id_, this); + // Take a reference on behalf of the RenderThread. This will be balanced + // when we receive ViewMsg_Close. + AddRef(); + + // If this is a popup, we must wait for the CreatingNew_ACK message before + // completing initialization. Otherwise, we can finish it now. + if (opener_id == MSG_ROUTING_NONE) { + did_show_ = true; + CompleteInit(parent_hwnd); + } + + host_window_ = parent_hwnd; + modal_dialog_event_.Set(modal_dialog_event); + + CommandLine command_line; + enable_dom_automation_ = + command_line.HasSwitch(switches::kDomAutomationController); + disable_popup_blocking_ = + command_line.HasSwitch(switches::kDisablePopupBlocking); + + debug_message_handler_ = new DebugMessageHandler(this); + RenderThread::current()->AddFilter(debug_message_handler_); +} + +void RenderView::OnMessageReceived(const IPC::Message& message) { + // Let the resource dispatcher intercept resource messages first. + if (resource_dispatcher_->OnMessageReceived(message)) + return; + IPC_BEGIN_MESSAGE_MAP(RenderView, message) + IPC_MESSAGE_HANDLER(ViewMsg_CreatingNew_ACK, OnCreatingNewAck) + IPC_MESSAGE_HANDLER(ViewMsg_CaptureThumbnail, SendThumbnail) + IPC_MESSAGE_HANDLER(ViewMsg_GetPrintedPagesCount, OnGetPrintedPagesCount) + IPC_MESSAGE_HANDLER(ViewMsg_PrintPages, OnPrintPages) + IPC_MESSAGE_HANDLER(ViewMsg_Navigate, OnNavigate) + IPC_MESSAGE_HANDLER(ViewMsg_Stop, OnStop) + IPC_MESSAGE_HANDLER(ViewMsg_LoadAlternateHTMLText, OnLoadAlternateHTMLText) + IPC_MESSAGE_HANDLER(ViewMsg_StopFinding, OnStopFinding) + IPC_MESSAGE_HANDLER(ViewMsg_Undo, OnUndo) + IPC_MESSAGE_HANDLER(ViewMsg_Redo, OnRedo) + IPC_MESSAGE_HANDLER(ViewMsg_Cut, OnCut) + IPC_MESSAGE_HANDLER(ViewMsg_Copy, OnCopy) + IPC_MESSAGE_HANDLER(ViewMsg_Paste, OnPaste) + IPC_MESSAGE_HANDLER(ViewMsg_Replace, OnReplace) + IPC_MESSAGE_HANDLER(ViewMsg_Delete, OnDelete) + IPC_MESSAGE_HANDLER(ViewMsg_SelectAll, OnSelectAll) + IPC_MESSAGE_HANDLER(ViewMsg_CopyImageAt, OnCopyImageAt) + IPC_MESSAGE_HANDLER(ViewMsg_Find, OnFind) + IPC_MESSAGE_HANDLER(ViewMsg_AlterTextSize, OnAlterTextSize) + IPC_MESSAGE_HANDLER(ViewMsg_SetPageEncoding, OnSetPageEncoding) + IPC_MESSAGE_HANDLER(ViewMsg_InspectElement, OnInspectElement) + IPC_MESSAGE_HANDLER(ViewMsg_ShowJavaScriptConsole, OnShowJavaScriptConsole) + IPC_MESSAGE_HANDLER(ViewMsg_DownloadImage, OnDownloadImage) + IPC_MESSAGE_HANDLER(ViewMsg_ScriptEvalRequest, OnScriptEvalRequest) + IPC_MESSAGE_HANDLER(ViewMsg_AddMessageToConsole, OnAddMessageToConsole) + IPC_MESSAGE_HANDLER(ViewMsg_DebugAttach, OnDebugAttach) + IPC_MESSAGE_HANDLER(ViewMsg_ReservePageIDRange, OnReservePageIDRange) + IPC_MESSAGE_HANDLER(ViewMsg_UploadFile, OnUploadFileRequest) + IPC_MESSAGE_HANDLER(ViewMsg_FormFill, OnFormFill) + IPC_MESSAGE_HANDLER(ViewMsg_FillPasswordForm, OnFillPasswordForm) + IPC_MESSAGE_HANDLER(ViewMsg_DragTargetDragEnter, OnDragTargetDragEnter) + IPC_MESSAGE_HANDLER(ViewMsg_DragTargetDragOver, OnDragTargetDragOver) + IPC_MESSAGE_HANDLER(ViewMsg_DragTargetDragLeave, OnDragTargetDragLeave) + IPC_MESSAGE_HANDLER(ViewMsg_DragTargetDrop, OnDragTargetDrop) + IPC_MESSAGE_HANDLER(ViewMsg_AllowDomAutomationBindings, + OnAllowDomAutomationBindings) + IPC_MESSAGE_HANDLER(ViewMsg_AllowDOMUIBindings, OnAllowDOMUIBindings) + IPC_MESSAGE_HANDLER(ViewMsg_SetDOMUIProperty, OnSetDOMUIProperty) + IPC_MESSAGE_HANDLER(ViewMsg_DragSourceEndedOrMoved, OnDragSourceEndedOrMoved) + IPC_MESSAGE_HANDLER(ViewMsg_DragSourceSystemDragEnded, + OnDragSourceSystemDragEnded) + IPC_MESSAGE_HANDLER(ViewMsg_SetInitialFocus, OnSetInitialFocus) + IPC_MESSAGE_HANDLER(ViewMsg_FindReplyACK, OnFindReplyAck) + IPC_MESSAGE_HANDLER(ViewMsg_UpdateTargetURL_ACK, OnUpdateTargetURLAck) + IPC_MESSAGE_HANDLER(ViewMsg_UpdateWebPreferences, OnUpdateWebPreferences) + IPC_MESSAGE_HANDLER(ViewMsg_SetAltErrorPageURL, OnSetAltErrorPageURL) + IPC_MESSAGE_HANDLER(ViewMsg_InstallMissingPlugin, OnInstallMissingPlugin) + IPC_MESSAGE_HANDLER(ViewMsg_RunFileChooserResponse, OnFileChooserResponse) + IPC_MESSAGE_HANDLER(ViewMsg_EnableViewSourceMode, OnEnableViewSourceMode) + IPC_MESSAGE_HANDLER(ViewMsg_UpdateBackForwardListCount, + OnUpdateBackForwardListCount) + IPC_MESSAGE_HANDLER(ViewMsg_GetAllSavableResourceLinksForCurrentPage, + OnGetAllSavableResourceLinksForCurrentPage) + IPC_MESSAGE_HANDLER(ViewMsg_GetSerializedHtmlDataForCurrentPageWithLocalLinks, + OnGetSerializedHtmlDataForCurrentPageWithLocalLinks) + IPC_MESSAGE_HANDLER(ViewMsg_GetApplicationInfo, OnGetApplicationInfo) + IPC_MESSAGE_HANDLER(ViewMsg_ShouldClose, OnMsgShouldClose) + IPC_MESSAGE_HANDLER(ViewMsg_ClosePage, OnClosePage) + IPC_MESSAGE_HANDLER(ViewMsg_ThemeChanged, OnThemeChanged) + // Have the super handle all other messages. + IPC_MESSAGE_UNHANDLED(RenderWidget::OnMessageReceived(message)) + IPC_END_MESSAGE_MAP() +} + +// Got a response from the browser after the renderer decided to create a new +// view. +void RenderView::OnCreatingNewAck(HWND parent) { + CompleteInit(parent); +} + +void RenderView::SendThumbnail() { + WebFrame* main_frame = webview()->GetMainFrame(); + if (!main_frame) + return; + + // get the URL for this page + GURL url(main_frame->GetURL()); + if (url.is_empty()) + return; + + if (size_.IsEmpty()) + return; // Don't create an empty thumbnail! + + ThumbnailScore score; + SkBitmap thumbnail; + CaptureThumbnail(main_frame, kThumbnailWidth, kThumbnailHeight, &thumbnail, + &score); + // send the thumbnail message to the browser process + IPC::Message* thumbnail_msg = new IPC::Message(routing_id_, + ViewHostMsg_Thumbnail::ID, IPC::Message::PRIORITY_NORMAL); + IPC::ParamTraits<GURL>::Write(thumbnail_msg, url); + IPC::ParamTraits<ThumbnailScore>::Write(thumbnail_msg, score); + IPC::ParamTraits<SkBitmap>::Write(thumbnail_msg, thumbnail); + Send(thumbnail_msg); +} + +int RenderView::SwitchFrameToPrintMediaType(const ViewMsg_Print_Params& params, + WebFrame* frame) { + float ratio = static_cast<float>(params.desired_dpi / params.dpi); + float paper_width = params.printable_size.width() * ratio; + float paper_height = params.printable_size.height() * ratio; + float minLayoutWidth = static_cast<float>(paper_width * params.min_shrink); + float maxLayoutWidth = static_cast<float>(paper_width * params.max_shrink); + + // Safari uses: 765 & 1224. Margins aren't exactly the same either. + // Scale = 2.222 for MDI printer. + int pages; + if (!frame->SetPrintingMode(true, + minLayoutWidth, + maxLayoutWidth, + &printed_document_width_)) { + NOTREACHED(); + pages = 0; + } else { + // Force to recalculate the height, otherwise it reuse the current window + // height as the default. + float effective_shrink = printed_document_width_ / paper_width; + gfx::Size page_size(printed_document_width_, + static_cast<int>(paper_height * effective_shrink) - 1); + WebView* view = frame->GetView(); + if (view) { + // Hack around an issue where if the current view height is higher than + // the page height, empty pages will be printed even if the bottom of the + // web page is empty. + printing_view_size_ = view->GetSize(); + view->Resize(page_size); + view->Layout(); + } + pages = frame->ComputePageRects(params.printable_size); + DCHECK(pages); + } + return pages; +} + +void RenderView::SwitchFrameToDisplayMediaType(WebFrame* frame) { + // Set the layout back to "normal" document; i.e. CSS media type = "screen". + frame->SetPrintingMode(false, 0, 0, NULL); + WebView* view = frame->GetView(); + if (view) { + // Restore from the hack described at SwitchFrameToPrintMediaType(). + view->Resize(printing_view_size_); + view->Layout(); + printing_view_size_.SetSize(0, 0); + } + printed_document_width_ = 0; +} + +void RenderView::OnPrintPage(const ViewMsg_PrintPage_Params& params) { + DCHECK(webview()); + if (webview()) + PrintPage(params, webview()->GetMainFrame()); +} + +void RenderView::PrintPage(const ViewMsg_PrintPage_Params& params, + WebFrame* frame) { + if (printed_document_width_ <= 0) { + NOTREACHED(); + return; + } + + // Generate a memory-based EMF file. The EMF will use the current screen's + // DPI. + gfx::Emf emf; + + emf.CreateDc(NULL, NULL); + HDC hdc = emf.hdc(); + DCHECK(hdc); + gfx::PlatformDevice::InitializeDC(hdc); + + gfx::Rect rect; + frame->GetPageRect(params.page_number, &rect); + DCHECK(rect.height()); + DCHECK(rect.width()); + double shrink = static_cast<double>(printed_document_width_) / + params.params.printable_size.width(); + // This check would fire each time the page would get truncated on the + // right. This is not worth a DCHECK() but should be looked into, for + // example, wouldn't be worth trying in landscape? + // DCHECK_LE(rect.width(), printed_document_width_); + + // Buffer one page at a time. + int src_size_x = printed_document_width_; + int src_size_y = + static_cast<int>(ceil(params.params.printable_size.height() * + shrink)); +#if 0 + // TODO(maruel): This code is kept for testing until the 100% GDI drawing + // code is stable. maruels use this code's output as a reference when the + // GDI drawing code fails. + + // Mix of Skia and GDI based. + gfx::PlatformCanvas canvas(src_size_x, src_size_y, true); + canvas.drawARGB(255, 255, 255, 255, SkPorterDuff::kSrc_Mode); + PlatformContextSkia context(&canvas); + if (!frame->SpoolPage(params.page_number, &context)) { + NOTREACHED() << "Printing page " << params.page_number << " failed."; + return; + } + + // Create a BMP v4 header that we can serialize. + BITMAPV4HEADER bitmap_header; + gfx::CreateBitmapV4Header(src_size_x, src_size_y, &bitmap_header); + const SkBitmap& src_bmp = canvas.getDevice()->accessBitmap(true); + SkAutoLockPixels src_lock(src_bmp); + int retval = StretchDIBits(hdc, + 0, + 0, + src_size_x, src_size_y, + 0, 0, + src_size_x, src_size_y, + src_bmp.getPixels(), + reinterpret_cast<BITMAPINFO*>(&bitmap_header), + DIB_RGB_COLORS, + SRCCOPY); + DCHECK(retval != GDI_ERROR); +#else + // 100% GDI based. + gfx::VectorCanvas canvas(hdc, src_size_x, src_size_y); + PlatformContextSkia context(&canvas); + // Set the clipping region to be sure to not overflow. + SkRect clip_rect; + clip_rect.set(0, 0, SkIntToScalar(src_size_x), SkIntToScalar(src_size_y)); + canvas.clipRect(clip_rect); + if (!frame->SpoolPage(params.page_number, &context)) { + NOTREACHED() << "Printing page " << params.page_number << " failed."; + return; + } +#endif + + // Done printing. Close the device context to retrieve the compiled EMF. + if (!emf.CloseDc()) { + NOTREACHED() << "EMF failed"; + } + + // Get the size of the compiled EMF. + unsigned buf_size = emf.GetDataSize(); + DCHECK(buf_size > 128); + ViewHostMsg_DidPrintPage_Params page_params; + page_params.data_size = 0; + page_params.emf_data_handle = NULL; + page_params.page_number = params.page_number; + page_params.document_cookie = params.params.document_cookie; + page_params.actual_shrink = shrink; + SharedMemory shared_buf; + + // http://msdn2.microsoft.com/en-us/library/ms535522.aspx + // Windows 2000/XP: When a page in a spooled file exceeds approximately 350 + // MB, it can fail to print and not send an error message. + if (buf_size < 350*1024*1024) { + // Allocate a shared memory buffer to hold the generated EMF data. + if (shared_buf.Create(L"", false, false, buf_size) && + shared_buf.Map(buf_size)) { + // Copy the bits into shared memory. + if (emf.GetData(shared_buf.memory(), buf_size)) { + page_params.emf_data_handle = shared_buf.handle(); + page_params.data_size = buf_size; + } else { + NOTREACHED() << "GetData() failed"; + } + shared_buf.Unmap(); + } else { + NOTREACHED() << "Buffer allocation failed"; + } + } else { + NOTREACHED() << "Buffer too large: " << buf_size; + } + emf.CloseEmf(); + if (Send(new ViewHostMsg_DuplicateSection(routing_id_, + page_params.emf_data_handle, + &page_params.emf_data_handle))) { + Send(new ViewHostMsg_DidPrintPage(routing_id_, page_params)); + } +} + +void RenderView::OnGetPrintedPagesCount(const ViewMsg_Print_Params& params) { + DCHECK(webview()); + if (!webview()) { + Send(new ViewHostMsg_DidGetPrintedPagesCount(routing_id_, + params.document_cookie, + 0)); + return; + } + WebFrame* frame = webview()->GetMainFrame(); + int expected_pages = SwitchFrameToPrintMediaType(params, frame); + Send(new ViewHostMsg_DidGetPrintedPagesCount(routing_id_, + params.document_cookie, + expected_pages)); + SwitchFrameToDisplayMediaType(frame); +} + +void RenderView::OnPrintPages(const ViewMsg_PrintPages_Params& params) { + DCHECK(webview()); + if (webview()) + PrintPages(params, webview()->GetMainFrame()); +} + +void RenderView::PrintPages(const ViewMsg_PrintPages_Params& params, + WebFrame* frame) { + int pages = SwitchFrameToPrintMediaType(params.params, frame); + Send(new ViewHostMsg_DidGetPrintedPagesCount(routing_id_, + params.params.document_cookie, + pages)); + if (pages) { + ViewMsg_PrintPage_Params page_params; + page_params.params = params.params; + if (params.pages.empty()) { + for (int i = 0; i < pages; ++i) { + page_params.page_number = i; + PrintPage(page_params, frame); + } + } else { + for (size_t i = 0; i < params.pages.size(); ++i) { + page_params.page_number = params.pages[i]; + PrintPage(page_params, frame); + } + } + } + SwitchFrameToDisplayMediaType(frame); +} + +void RenderView::CapturePageInfo(int load_id, bool preliminary_capture) { + if (load_id != page_id_) + return; // this capture call is no longer relevant due to navigation + if (load_id == last_indexed_page_id_) + return; // we already indexed this page + + if (!webview()) + return; + + WebFrame* main_frame = webview()->GetMainFrame(); + if (!main_frame) + return; + + // Don't index/capture pages that are in view source mode. + if (main_frame->GetInViewSourceMode()) + return; + + // Don't index/capture pages that failed to load. This only checks the top + // level frame so the thumbnail may contain a frame that failed to load. + WebDataSource* ds = main_frame->GetDataSource(); + if (ds && ds->HasUnreachableURL()) + return; + + if (!preliminary_capture) + last_indexed_page_id_ = load_id; + + // get the URL for this page + GURL url(main_frame->GetURL()); + if (url.is_empty()) + return; + + // full text + std::wstring contents; + CaptureText(main_frame, &contents); + if (contents.size()) { + // Send the text to the browser for indexing. + Send(new ViewHostMsg_PageContents(url, load_id, contents)); + } + + // thumbnail + SendThumbnail(); +} + +void RenderView::CaptureText(WebFrame* frame, std::wstring* contents) { + contents->clear(); + if (!frame) + return; + +#ifdef TIME_TEXT_RETRIEVAL + double begin = time_util::GetHighResolutionTimeNow(); +#endif + + // get the contents of the frame + frame->GetContentAsPlainText(kMaxIndexChars, contents); + +#ifdef TIME_TEXT_RETRIEVAL + double end = time_util::GetHighResolutionTimeNow(); + char buf[128]; + sprintf_s(buf, "%d chars retrieved for indexing in %gms\n", + contents.size(), (end - begin)*1000); + OutputDebugStringA(buf); +#endif + + // When the contents are clipped to the maximum, we don't want to have a + // partial word indexed at the end that might have been clipped. Therefore, + // terminate the string at the last space to ensure no words are clipped. + if (contents->size() == kMaxIndexChars) { + size_t last_space_index = contents->find_last_of(kWhitespaceWide); + if (last_space_index == std::wstring::npos) + return; // don't index if we got a huge block of text with no spaces + contents->resize(last_space_index); + } +} + +void RenderView::CaptureThumbnail(WebFrame* frame, + int w, + int h, + SkBitmap* thumbnail, + ThumbnailScore* score) { +#ifdef TIME_BITMAP_RETRIEVAL + double begin = time_util::GetHighResolutionTimeNow(); +#endif + + gfx::BitmapPlatformDevice device(frame->CaptureImage(true)); + const SkBitmap& src_bmp = device.accessBitmap(false); + + SkRect dest_rect; + dest_rect.set(0, 0, SkIntToScalar(w), SkIntToScalar(h)); + float dest_aspect = dest_rect.width() / dest_rect.height(); + + // Get the src rect so that we can preserve the aspect ratio while filling + // the destination. + SkIRect src_rect; + if (src_bmp.width() < dest_rect.width() || + src_bmp.height() < dest_rect.height()) { + // Source image is smaller: we clip the part of source image within the + // dest rect, and then stretch it to fill the dest rect. We don't respect + // the aspect ratio in this case. + src_rect.set(0, 0, static_cast<S16CPU>(dest_rect.width()), + static_cast<S16CPU>(dest_rect.height())); + score->good_clipping = false; + } else { + float src_aspect = static_cast<float>(src_bmp.width()) / src_bmp.height(); + if (src_aspect > dest_aspect) { + // Wider than tall, clip horizontally: we center the smaller thumbnail in + // the wider screen. + S16CPU new_width = static_cast<S16CPU>(src_bmp.height() * dest_aspect); + S16CPU x_offset = (src_bmp.width() - new_width) / 2; + src_rect.set(x_offset, 0, new_width + x_offset, src_bmp.height()); + score->good_clipping = false; + } else { + src_rect.set(0, 0, src_bmp.width(), + static_cast<S16CPU>(src_bmp.width() / dest_aspect)); + score->good_clipping = true; + } + } + + score->at_top = (frame->ScrollOffset().height() == 0); + + SkBitmap subset; + device.accessBitmap(false).extractSubset(&subset, src_rect); + + // Resample the subset that we want to get it the right size. + *thumbnail = gfx::ImageOperations::Resize( + subset, gfx::ImageOperations::RESIZE_LANCZOS3, gfx::Size(w, h)); + + score->boring_score = CalculateBoringScore(thumbnail); + +#ifdef TIME_BITMAP_RETRIEVAL + double end = time_util::GetHighResolutionTimeNow(); + char buf[128]; + sprintf_s(buf, "thumbnail in %gms\n", (end - begin) * 1000); + OutputDebugStringA(buf); +#endif +} + +double RenderView::CalculateBoringScore(SkBitmap* bitmap) { + int histogram[256] = {0}; + color_utils::BuildLumaHistogram(bitmap, histogram); + + int color_count = *std::max_element(histogram, histogram + 256); + int pixel_count = bitmap->width() * bitmap->height(); + return static_cast<double>(color_count) / pixel_count; +} + +void RenderView::OnNavigate(const ViewMsg_Navigate_Params& params) { + if (!webview()) + return; + + AboutHandler::MaybeHandle(params.url); + + bool is_reload = params.reload; + + WebFrame* main_frame = webview()->GetMainFrame(); + if (is_reload && !main_frame->HasCurrentState()) { + // We cannot reload if we do not have any history state. This happens, for + // example, when recovering from a crash. Our workaround here is a bit of + // a hack since it means that reload after a crashed tab does not cause an + // end-to-end cache validation. + is_reload = false; + } + + WebRequestCachePolicy cache_policy; + if (is_reload) { + cache_policy = WebRequestReloadIgnoringCacheData; + } else if (params.page_id != -1 || main_frame->GetInViewSourceMode()) { + cache_policy = WebRequestReturnCacheDataElseLoad; + } else { + cache_policy = WebRequestUseProtocolCachePolicy; + } + + scoped_ptr<WebRequest> request(WebRequest::Create(params.url)); + request->SetCachePolicy(cache_policy); + request->SetExtraData(new RenderViewExtraRequestData( + params.page_id, params.transition, params.url)); + + // If we are reloading, then WebKit will use the state of the current page. + // Otherwise, we give it the state to navigate to. + if (!is_reload) + request->SetHistoryState(params.state); + + main_frame->LoadRequest(request.get()); +} + +// Stop loading the current page +void RenderView::OnStop() { + if (webview()) + webview()->StopLoading(); +} + +void RenderView::OnLoadAlternateHTMLText(const std::string& html_contents, + bool new_navigation, + const GURL& display_url, + const std::string& security_info) { + if (!webview()) + return; + + scoped_ptr<WebRequest> request(WebRequest::Create( + GURL(kUnreachableWebDataURL))); + request->SetSecurityInfo(security_info); + + webview()->GetMainFrame()->LoadAlternateHTMLString(request.get(), + html_contents, + display_url, + !new_navigation); +} + +void RenderView::OnCopyImageAt(int x, int y) { + webview()->CopyImageAt(x, y); +} + +void RenderView::OnInspectElement(int x, int y) { + webview()->InspectElement(x, y); +} + +void RenderView::OnShowJavaScriptConsole() { + webview()->ShowJavaScriptConsole(); +} + +void RenderView::OnStopFinding(bool clear_selection) { + WebView* view = webview(); + if (!view) + return; + + if (clear_selection) + view->GetFocusedFrame()->ClearSelection(); + + WebFrame* frame = view->GetMainFrame(); + while (frame) { + frame->StopFinding(); + frame = view->GetNextFrameAfter(frame, false); + } +} + +void RenderView::OnFindReplyAck() { + // Check if there is any queued up request waiting to be sent. + if (queued_find_reply_message_.get()) { + // Send the search result over to the browser process. + Send(queued_find_reply_message_.get()); + queued_find_reply_message_.release(); + } +} + +void RenderView::OnUpdateTargetURLAck() { + // Check if there is a targeturl waiting to be sent. + if (target_url_status_ == TARGET_PENDING) { + Send(new ViewHostMsg_UpdateTargetURL(routing_id_, page_id_, + pending_target_url_)); + } + + target_url_status_ = TARGET_NONE; +} + +void RenderView::OnUndo() { + if (!webview()) + return; + + webview()->GetFocusedFrame()->Undo(); +} + +void RenderView::OnRedo() { + if (!webview()) + return; + + webview()->GetFocusedFrame()->Redo(); +} + +void RenderView::OnCut() { + if (!webview()) + return; + + webview()->GetFocusedFrame()->Cut(); +} + +void RenderView::OnCopy() { + if (!webview()) + return; + + webview()->GetFocusedFrame()->Copy(); +} + +void RenderView::OnPaste() { + if (!webview()) + return; + + webview()->GetFocusedFrame()->Paste(); +} + +void RenderView::OnReplace(const std::wstring& text) { + if (!webview()) + return; + + webview()->GetFocusedFrame()->Replace(text); +} + +void RenderView::OnDelete() { + if (!webview()) + return; + + webview()->GetFocusedFrame()->Delete(); +} + +void RenderView::OnSelectAll() { + if (!webview()) + return; + + webview()->GetFocusedFrame()->SelectAll(); +} + +void RenderView::OnSetInitialFocus(bool reverse) { + if (!webview()) + return; + webview()->SetInitialFocus(reverse); +} + +/////////////////////////////////////////////////////////////////////////////// + +// Tell the embedding application that the URL of the active page has changed +void RenderView::UpdateURL(WebFrame* frame) { + WebDataSource* ds = frame->GetDataSource(); + DCHECK(ds); + + const WebRequest& request = ds->GetRequest(); + const WebRequest& initial_request = ds->GetInitialRequest(); + const WebResponse& response = ds->GetResponse(); + + // We don't hold a reference to the extra data. The request's reference will + // be sufficient because we won't modify it during our call. MAY BE NULL. + RenderViewExtraRequestData* extra_data = + static_cast<RenderViewExtraRequestData*>(request.GetExtraData()); + + ViewHostMsg_FrameNavigate_Params params; + params.is_post = false; + params.page_id = page_id_; + if (!request.GetSecurityInfo().empty()) { + // SSL state specified in the request takes precedence over the one in the + // response. + // So far this is only intended for error pages that are not expected to be + // over ssl, so we should not get any clash. + DCHECK(response.GetSecurityInfo().empty()); + params.security_info = request.GetSecurityInfo(); + } else { + params.security_info = response.GetSecurityInfo(); + } + + // Set the URL to be displayed in the browser UI to the user. + if (ds->HasUnreachableURL()) { + params.url = ds->GetUnreachableURL(); + } else { + params.url = request.GetURL(); + } + + params.redirects = ds->GetRedirectChain(); + params.should_update_history = !ds->HasUnreachableURL(); + + const SearchableFormData* searchable_form_data = + frame->GetDataSource()->GetSearchableFormData(); + if (searchable_form_data) { + params.searchable_form_url = searchable_form_data->url(); + params.searchable_form_element_name = searchable_form_data->element_name(); + params.searchable_form_encoding = searchable_form_data->encoding(); + } + + const PasswordForm* password_form_data = + frame->GetDataSource()->GetPasswordFormData(); + if (password_form_data) + params.password_form = *password_form_data; + + params.gesture = navigation_gesture_; + navigation_gesture_ = NavigationGestureUnknown; + + if (webview()->GetMainFrame() == frame) { + // Top-level navigation. + + // Update contents MIME type for main frame. + std::wstring mime_type = ds->GetResponseMimeType(); + params.contents_mime_type = WideToASCII(mime_type); + + // We assume top level navigations initiated by the renderer are link + // clicks. + params.transition = extra_data ? + extra_data->transition_type : PageTransition::LINK; + if (!PageTransition::IsMainFrame(params.transition)) { + // If the main frame does a load, it should not be reported as a subframe + // navigation. This can occur in the following case: + // 1. You're on a site with frames. + // 2. You do a subframe navigation. This is stored with transition type + // MANUAL_SUBFRAME. + // 3. You navigate to some non-frame site, say, google.com. + // 4. You navigate back to the page from step 2. Since it was initially + // MANUAL_SUBFRAME, it will be that same transition type here. + // We don't want that, because any navigation that changes the toplevel + // frame should be tracked as a toplevel navigation (this allows us to + // update the URL bar, etc). + params.transition = PageTransition::LINK; + } + + if (params.transition == PageTransition::LINK && + frame->GetDataSource()->IsFormSubmit()) { + params.transition = PageTransition::FORM_SUBMIT; + } + + // If we have a valid consumed client redirect source, + // the page contained a client redirect (meta refresh, document.loc...), + // so we set the referrer and transition to match. + if (completed_client_redirect_src_.is_valid()) { + params.referrer = completed_client_redirect_src_; + params.transition = static_cast<PageTransition::Type>( + params.transition | PageTransition::CLIENT_REDIRECT); + } else { + // Bug 654101: the referrer will be empty on https->http transitions. It + // would be nice if we could get the real referrer from somewhere. + params.referrer = GURL(initial_request.GetHttpReferrer()); + } + + std::wstring method = request.GetHttpMethod(); + if (method == L"POST") + params.is_post = true; + + Send(new ViewHostMsg_FrameNavigate(routing_id_, params)); + } else { + // Subframe navigation: the type depends on whether this navigation + // generated a new session history entry. When they do generate a session + // history entry, it means the user initiated the navigation and we should + // mark it as such. This test checks if this is the first time UpdateURL + // has been called since WillNavigateToURL was called to initiate the load. + if (page_id_ > last_page_id_sent_to_browser_) + params.transition = PageTransition::MANUAL_SUBFRAME; + else + params.transition = PageTransition::AUTO_SUBFRAME; + + // The browser should never initiate a subframe navigation. + DCHECK(!extra_data); + Send(new ViewHostMsg_FrameNavigate(routing_id_, params)); + } + + last_page_id_sent_to_browser_ = + std::max(last_page_id_sent_to_browser_, page_id_); + + // If we end up reusing this WebRequest (for example, due to a #ref click), + // we don't want the transition type to persist. + if (extra_data) + extra_data->transition_type = PageTransition::LINK; // Just clear it. +} + +// Tell the embedding application that the title of the active page has changed +void RenderView::UpdateTitle(WebFrame* frame, const std::wstring& title) { + // Ignore all but top level navigations... + if (webview()->GetMainFrame() == frame) + Send(new ViewHostMsg_UpdateTitle(routing_id_, page_id_, title)); +} + +void RenderView::UpdateEncoding(WebFrame* frame, + const std::wstring& encoding_name) { + // Only update main frame's encoding_name. + if (webview()->GetMainFrame() == frame && + last_encoding_name_ != encoding_name) { + // save the encoding name for later comparing. + last_encoding_name_ = encoding_name; + + Send(new ViewHostMsg_UpdateEncoding(routing_id_, + last_encoding_name_)); + } +} + +void RenderView::UpdateSessionHistory(WebFrame* frame) { + // If we have a valid page ID at this point, then it corresponds to the page + // we are navigating away from. Otherwise, this is the first navigation, so + // there is no past session history to record. + if (page_id_ == -1) + return; + + GURL url; + std::wstring title; + std::string state; + if (!webview()->GetMainFrame()->GetPreviousState(&url, &title, &state)) + return; + + Send(new ViewHostMsg_UpdateState(routing_id_, page_id_, url, title, state)); +} + +/////////////////////////////////////////////////////////////////////////////// +// WebViewDelegate + +void RenderView::DidStartLoading(WebView* webview) { + if (is_loading_) { + DLOG(WARNING) << "DidStartLoading called while loading"; + return; + } + + is_loading_ = true; + // Clear the pointer so that we can assign it only when there is an unknown + // plugin on a page. + first_default_plugin_ = NULL; + + Send(new ViewHostMsg_DidStartLoading(routing_id_, page_id_)); +} + +void RenderView::DidStopLoading(WebView* webview) { + if (!is_loading_) { + DLOG(WARNING) << "DidStopLoading called while not loading"; + return; + } + + is_loading_ = false; + + // NOTE: For now we're doing the safest thing, and sending out notification + // when done loading. This currently isn't an issue as the favicon is only + // displayed when done loading. Ideally we would send notification when + // finished parsing the head, but webkit doesn't support that yet. + // The feed discovery code would also benefit from access to the head. + GURL favicon_url(webview->GetMainFrame()->GetFavIconURL()); + if (!favicon_url.is_empty()) + Send(new ViewHostMsg_UpdateFavIconURL(routing_id_, page_id_, favicon_url)); + + AddGURLSearchProvider(webview->GetMainFrame()->GetOSDDURL(), + true); // autodetected + + Send(new ViewHostMsg_DidStopLoading(routing_id_, page_id_)); + + MessageLoop::current()->PostDelayedTask(FROM_HERE, + method_factory_.NewRunnableMethod(&RenderView::CapturePageInfo, page_id_, + false), + kDelayForCaptureMs); + + // The page is loaded. Try to process the file we need to upload if any. + ProcessPendingUpload(); + + // Since the page is done loading, we are sure we don't need to try + // again. + ResetPendingUpload(); +} + +void RenderView::DidStartProvisionalLoadForFrame( + WebView* webview, + WebFrame* frame, + NavigationGesture gesture) { + if (webview->GetMainFrame() == frame) + navigation_gesture_ = gesture; + + Send(new ViewHostMsg_DidStartProvisionalLoadForFrame( + routing_id_, webview->GetMainFrame() == frame, + frame->GetProvisionalDataSource()->GetRequest().GetURL())); +} + +bool RenderView::DidLoadResourceFromMemoryCache(WebView* webview, + const WebRequest& request, + const WebResponse& response, + WebFrame* frame) { + // Let the browser know we loaded a resource from the memory cache. This + // message is needed to display the correct SSL indicators. + Send(new ViewHostMsg_DidLoadResourceFromMemoryCache(routing_id_, + request.GetURL(), response.GetSecurityInfo())); + + return false; +} + +void RenderView::DidReceiveProvisionalLoadServerRedirect(WebView* webview, + WebFrame* frame) { + if (frame == webview->GetMainFrame()) { + // Received a redirect on the main frame. + WebDataSource* data_source = + webview->GetMainFrame()->GetProvisionalDataSource(); + if (!data_source) { + // Should only be invoked when we have a data source. + NOTREACHED(); + return; + } + const std::vector<GURL>& redirects = data_source->GetRedirectChain(); + if (redirects.size() >= 2) { + Send(new ViewHostMsg_DidRedirectProvisionalLoad( + routing_id_, page_id_, redirects[redirects.size() - 2], + redirects[redirects.size() - 1])); + } + } +} + +void RenderView::DidFailProvisionalLoadWithError(WebView* webview, + const WebError& error, + WebFrame* frame) { + // Notify the browser that we failed a provisional load with an error. + // + // Note: It is important this notification occur before DidStopLoading so the + // SSL manager can react to the provisional load failure before being + // notified the load stopped. + // + WebDataSource* ds = frame->GetProvisionalDataSource(); + DCHECK(ds); + + const WebRequest& failed_request = ds->GetRequest(); + + bool show_repost_interstitial = + (error.GetErrorCode() == net::ERR_CACHE_MISS && + LowerCaseEqualsASCII(failed_request.GetHttpMethod(), "post")); + Send(new ViewHostMsg_DidFailProvisionalLoadWithError( + routing_id_, frame == webview->GetMainFrame(), + error.GetErrorCode(), error.GetFailedURL(), + show_repost_interstitial)); + + // TODO(darin): This should not be necessary! + if (is_loading_) + DidStopLoading(webview); + + // Don't display an error page if this is simply a cancelled load. Aside + // from being dumb, WebCore doesn't expect it and it will cause a crash. + if (error.GetErrorCode() == net::ERR_ABORTED) + return; + + // If this is a failed back/forward/reload navigation, then we need to do a + // 'replace' load. This is necessary to avoid messing up session history. + // Otherwise, we do a normal load, which simulates a 'go' navigation as far + // as session history is concerned. + RenderViewExtraRequestData* extra_data = + static_cast<RenderViewExtraRequestData*>(failed_request.GetExtraData()); + bool replace = extra_data && !extra_data->is_new_navigation(); + + const GURL& failed_url = error.GetFailedURL(); + const GURL& error_page_url = GetAlternateErrorPageURL(failed_url, + WebViewDelegate::DNS_ERROR); + if (error.GetErrorCode() == net::ERR_NAME_NOT_RESOLVED && + error_page_url.is_valid()) { + // Ask the WebFrame to fetch the alternate error page for us. + frame->LoadAlternateHTMLErrorPage(&failed_request, error, error_page_url, + replace, GURL(kUnreachableWebDataURL)); + } else { + LoadNavigationErrorPage(frame, &failed_request, error, std::string(), + replace); + } +} + +void RenderView::LoadNavigationErrorPage(WebFrame* frame, + const WebRequest* failed_request, + const WebError& error, + const std::string& html, + bool replace) { + const GURL& failed_url = error.GetFailedURL(); + + std::string alt_html; + if (html.empty()) { + // Use a local error page. + int resource_id; + DictionaryValue error_strings; + if (error.GetErrorCode() == net::ERR_CACHE_MISS && + LowerCaseEqualsASCII(failed_request->GetHttpMethod(), "post")) { + GetFormRepostErrorValues(failed_url, &error_strings); + resource_id = IDR_ERROR_NO_DETAILS_HTML; + } else { + GetLocalizedErrorValues(error, &error_strings); + resource_id = IDR_NET_ERROR_HTML; + } + error_strings.SetString(L"textdirection", + (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) ? + L"rtl" : L"ltr"); + + alt_html = GetAltHTMLForTemplate(error_strings, resource_id); + } else { + alt_html = html; + } + + // Use a data: URL as the site URL to prevent against XSS attacks. + scoped_ptr<WebRequest> request(failed_request->Clone()); + request->SetURL(GURL(kUnreachableWebDataURL)); + + frame->LoadAlternateHTMLString(request.get(), alt_html, failed_url, + replace); +} + +void RenderView::DidCommitLoadForFrame(WebView *webview, WebFrame* frame, + bool is_new_navigation) { + const WebRequest& request = + webview->GetMainFrame()->GetDataSource()->GetRequest(); + RenderViewExtraRequestData* extra_data = + static_cast<RenderViewExtraRequestData*>(request.GetExtraData()); + + if (is_new_navigation) { + // When we perform a new navigation, we need to update the previous session + // history entry with state for the page we are leaving. + UpdateSessionHistory(frame); + + // We bump our Page ID to correspond with the new session history entry. + page_id_ = next_page_id_++; + + MessageLoop::current()->PostDelayedTask(FROM_HERE, + method_factory_.NewRunnableMethod(&RenderView::CapturePageInfo, + page_id_, true), + kDelayForForcedCaptureMs); + } else { + // Inspect the extra_data on the main frame (set in our Navigate method) to + // see if the navigation corresponds to a session history navigation... + // Note: |frame| may or may not be the toplevel frame, but for the case + // of capturing session history, the first committed frame suffices. We + // keep track of whether we've seen this commit before so that only capture + // session history once per navigation. + if (extra_data && !extra_data->is_new_navigation() && + !extra_data->request_committed) { + // This is a successful session history navigation! + UpdateSessionHistory(frame); + + page_id_ = extra_data->pending_page_id(); + } + } + + // Remember that we've already processed this request, so we don't update + // the session history again. We do this regardless of whether this is + // a session history navigation, because if we attempted a session history + // navigation without valid HistoryItem state, WebCore will think it is a + // new navigation. + if (extra_data) + extra_data->request_committed = true; + + UpdateURL(frame); + + // If this committed load was initiated by a client redirect, we're + // at the last stop now, so clear it. + completed_client_redirect_src_ = GURL(); + + // Check whether we have new encoding name. + UpdateEncoding(frame, webview->GetMainFrameEncodingName()); +} + +void RenderView::DidReceiveTitle(WebView* webview, + const std::wstring& title, + WebFrame* frame) { + UpdateTitle(frame, title); + + // Also check whether we have new encoding name. + UpdateEncoding(frame, webview->GetMainFrameEncodingName()); +} + +void RenderView::DidFinishLoadForFrame(WebView* webview, WebFrame* frame) { +} + +void RenderView::DidFailLoadWithError(WebView* webview, + const WebError& error, + WebFrame* frame) { +} + +void RenderView::DidFinishDocumentLoadForFrame(WebView* webview, + WebFrame* frame) { + // Check whether we have new encoding name. + UpdateEncoding(frame, webview->GetMainFrameEncodingName()); +} + +void RenderView::DidHandleOnloadEventsForFrame(WebView* webview, + WebFrame* frame) { +} + +void RenderView::DidChangeLocationWithinPageForFrame(WebView* webview, + WebFrame* frame, + bool is_new_navigation) { + DidCommitLoadForFrame(webview, frame, is_new_navigation); +} + +void RenderView::DidReceiveIconForFrame(WebView* webview, + WebFrame* frame) { +} + +void RenderView::WillPerformClientRedirect(WebView* webview, + WebFrame* frame, + const GURL& src_url, + const GURL& dest_url, + unsigned int delay_seconds, + unsigned int fire_date) { +} + +void RenderView::DidCancelClientRedirect(WebView* webview, + WebFrame* frame) { +} + +void RenderView::DidCompleteClientRedirect(WebView* webview, + WebFrame* frame, + const GURL& source) { + if (webview->GetMainFrame() == frame) + completed_client_redirect_src_ = source; +} + +void RenderView::BindDOMAutomationController(WebFrame* webframe) { + dom_automation_controller_.set_message_sender(this); + dom_automation_controller_.set_routing_id(routing_id_); + dom_automation_controller_.BindToJavascript(webframe, + L"domAutomationController"); +} + +void RenderView::WindowObjectCleared(WebFrame* webframe) { + external_js_object_.set_render_view(this); + external_js_object_.BindToJavascript(webframe, L"external"); + if (enable_dom_automation_) + BindDOMAutomationController(webframe); + if (enable_dom_ui_bindings_) { + dom_ui_bindings_.set_message_sender(this); + dom_ui_bindings_.set_routing_id(routing_id_); + dom_ui_bindings_.BindToJavascript(webframe, L"chrome"); + } +} + +WindowOpenDisposition RenderView::DispositionForNavigationAction( + WebView* webview, + WebFrame* frame, + const WebRequest* request, + WebNavigationType type, + WindowOpenDisposition disposition, + bool is_redirect) { + // Webkit is asking whether to navigate to a new URL. + // This is fine normally, except if we're showing UI from one security + // context and they're trying to navigate to a different context. + const GURL& url = request->GetURL(); + // We only care about navigations that are within the current tab (as opposed + // to, for example, opening a new window). + // But we sometimes navigate to about:blank to clear a tab, and we want to + // still allow that. + if (disposition == CURRENT_TAB && !(url.SchemeIs("about"))) { + // GetExtraData is NULL when we did not issue the request ourselves (see + // OnNavigate), and so such a request may correspond to a link-click, + // script, or drag-n-drop initiated navigation. + if (frame == webview->GetMainFrame() && !request->GetExtraData()) { + // When we received such unsolicited navigations, we sometimes want to + // punt them up to the browser to handle. + if (enable_dom_ui_bindings_ || + frame->GetInViewSourceMode() || + url.SchemeIs("view-source")) { + OpenURL(webview, url, disposition); + return IGNORE_ACTION; // Suppress the load here. + } + } + } + + // Detect when a page is "forking" a new tab that can be safely rendered in + // its own process. This is done by sites like Gmail that try to open links + // in new windows without script connections back to the original page. We + // treat such cases as browser navigations (in which we will create a new + // renderer for a cross-site navigation), rather than WebKit navigations. + // + // We use the following heuristic to decide whether to fork a new page in its + // own process: + // The parent page must open a new tab to about:blank, set the new tab's + // window.opener to null, and then redirect the tab to a cross-site URL using + // JavaScript. + bool is_fork = + // Must start from a tab showing about:blank, which is later redirected. + frame->GetURL() == GURL("about:blank") && + // Must be the first real navigation of the tab. + GetHistoryBackListCount() < 1 && + GetHistoryForwardListCount() < 1 && + // The parent page must have set the child's window.opener to null before + // redirecting to the desired URL. + frame->GetOpener() == NULL && + // Must be a top-level frame. + frame->GetParent() == NULL && + // Must not have issued the request from this page. GetExtraData is NULL + // when the navigation is being done by something outside the page. + !request->GetExtraData() && + // Must be targeted at the current tab. + disposition == CURRENT_TAB && + // Must be a JavaScript navigation, which appears as "other". + type == WebNavigationTypeOther; + if (is_fork) { + // Open the URL via the browser, not via WebKit. + OpenURL(webview, url, disposition); + return IGNORE_ACTION; + } + + return disposition; +} + +void RenderView::RunJavaScriptAlert(WebView* webview, + const std::wstring& message) { + RunJavaScriptMessage(MessageBoxView::kIsJavascriptAlert, + message, + std::wstring(), + NULL); +} + +bool RenderView::RunJavaScriptConfirm(WebView* webview, + const std::wstring& message) { + return RunJavaScriptMessage(MessageBoxView::kIsJavascriptConfirm, + message, + std::wstring(), + NULL); +} + +bool RenderView::RunJavaScriptPrompt(WebView* webview, + const std::wstring& message, + const std::wstring& default_value, + std::wstring* result) { + return RunJavaScriptMessage(MessageBoxView::kIsJavascriptPrompt, + message, + default_value, + result); +} + +bool RenderView::RunJavaScriptMessage(int type, + const std::wstring& message, + const std::wstring& default_value, + std::wstring* result) { + bool success = false; + std::wstring result_temp; + if (!result) + result = &result_temp; + IPC::SyncMessage* msg = new ViewHostMsg_RunJavaScriptMessage( + routing_id_, message, default_value, type, &success, result); + + msg->set_pump_messages_event(modal_dialog_event_); + Send(msg); + + return success; +} + +void RenderView::AddGURLSearchProvider(const GURL& osd_url, bool autodetected) { + if (!osd_url.is_empty()) + Send(new ViewHostMsg_PageHasOSDD(routing_id_, page_id_, osd_url, + autodetected)); +} + +bool RenderView::RunBeforeUnloadConfirm(WebView* webview, + const std::wstring& message) { + bool success = false; + // This is an ignored return value, but is included so we can accept the same + // response as RunJavaScriptMessage. + std::wstring ignored_result; + IPC::SyncMessage* msg = new ViewHostMsg_RunBeforeUnloadConfirm( + routing_id_, message, &success, &ignored_result); + + msg->set_pump_messages_event(modal_dialog_event_); + Send(msg); + + return success; +} + +void RenderView::OnUnloadListenerChanged(WebView* webview, WebFrame* webframe) { + bool has_listener = false; + if (!has_unload_listener_) { + has_listener = webframe->HasUnloadListener(); + } else { + WebFrame* frame = webview->GetMainFrame(); + while (frame != NULL) { + if (frame->HasUnloadListener()) { + has_listener = true; + break; + } + frame = webview->GetNextFrameAfter(frame, false); + } + } + if (has_listener != has_unload_listener_) { + has_unload_listener_ = has_listener; + Send(new ViewHostMsg_UnloadListenerChanged(routing_id_, has_listener)); + } +} + +void RenderView::ShowModalHTMLDialog(const GURL& url, int width, int height, + const std::string& json_arguments, + std::string* json_retval) { + IPC::SyncMessage* msg = new ViewHostMsg_ShowModalHTMLDialog( + routing_id_, url, width, height, json_arguments, json_retval); + + msg->set_pump_messages_event(modal_dialog_event_); + Send(msg); +} + +uint32 RenderView::GetCPBrowsingContext() { + uint32 context = 0; + Send(new ViewHostMsg_GetCPBrowsingContext(&context)); + return context; +} + +// Tell the browser to display a destination link. +void RenderView::UpdateTargetURL(WebView* webview, const GURL& url) { + if (url != target_url_) { + if (target_url_status_ == TARGET_INFLIGHT || + target_url_status_ == TARGET_PENDING) { + // If we have a request in-flight, save the URL to be sent when we + // receive an ACK to the in-flight request. We can happily overwrite + // any existing pending sends. + pending_target_url_ = url; + target_url_status_ = TARGET_PENDING; + } else { + Send(new ViewHostMsg_UpdateTargetURL(routing_id_, page_id_, url)); + target_url_ = url; + target_url_status_ = TARGET_INFLIGHT; + } + } +} + +void RenderView::RunFileChooser(const std::wstring& default_filename, + WebFileChooserCallback* file_chooser) { + if (file_chooser_.get()) { + // TODO(brettw): bug 1235154: This should be a synchronous message to deal + // with the fact that web pages can programatically trigger this. With the + // asnychronous messages, we can get an additional call when one is pending, + // which this test is for. For now, we just ignore the additional file + // chooser request. WebKit doesn't do anything to expect the callback, so + // we can just ignore calling it. + delete file_chooser; + return; + } + file_chooser_.reset(file_chooser); + Send(new ViewHostMsg_RunFileChooser(routing_id_, default_filename)); +} + +void RenderView::AddMessageToConsole(WebView* webview, + const std::wstring& message, + unsigned int line_no, + const std::wstring& source_id) { + Send(new ViewHostMsg_AddMessageToConsole(routing_id_, message, + static_cast<int32>(line_no), + source_id)); +} + +void RenderView::AddSearchProvider(const std::string& url) { + AddGURLSearchProvider(GURL(url), + false); // not autodetected +} + +void RenderView::DebuggerOutput(const std::wstring& out) { + Send(new ViewHostMsg_DebuggerOutput(routing_id_, out)); +} + +WebView* RenderView::CreateWebView(WebView* webview, bool user_gesture) { + int32 routing_id = MSG_ROUTING_NONE; + HANDLE modal_dialog_event; + bool result = RenderThread::current()->Send( + new ViewHostMsg_CreateView(routing_id_, user_gesture, &routing_id, + &modal_dialog_event)); + if (routing_id == MSG_ROUTING_NONE) { + DCHECK(modal_dialog_event == NULL); + return NULL; + } + + // The WebView holds a reference to this new RenderView + const WebPreferences& prefs = webview->GetPreferences(); + RenderView* view = RenderView::Create(NULL, modal_dialog_event, routing_id_, + prefs, routing_id); + view->set_opened_by_user_gesture(user_gesture); + + // Copy over the alternate error page URL so we can have alt error pages in + // the new render view (we don't need the browser to send the URL back down). + view->alternate_error_page_url_ = alternate_error_page_url_; + + return view->webview(); +} + +WebWidget* RenderView::CreatePopupWidget(WebView* webview) { + RenderWidget* widget = RenderWidget::Create(routing_id_); + return widget->webwidget(); +} + +WebPluginDelegate* RenderView::CreatePluginDelegate( + WebView* webview, + const GURL& url, + const std::string& mime_type, + const std::string& clsid, + std::string* actual_mime_type) { + if (RenderProcess::ShouldLoadPluginsInProcess()) { + std::wstring path; + RenderThread::current()->Send( + new ViewHostMsg_GetPluginPath(url, mime_type, clsid, &path, + actual_mime_type)); + if (path.empty()) + return NULL; + + std::string mime_type_to_use; + if (actual_mime_type && !actual_mime_type->empty()) + mime_type_to_use = *actual_mime_type; + else + mime_type_to_use = mime_type; + + return WebPluginDelegateImpl::Create(path, mime_type_to_use, host_window_); + } + + WebPluginDelegateProxy* proxy = + WebPluginDelegateProxy::Create(url, mime_type, clsid, this); + if (!proxy) + return NULL; + + // We hold onto the proxy so we can poke it when we are painting. See our + // DidPaint implementation below. + plugin_delegates_.push_back(proxy); + + return proxy; +} + +void RenderView::OnMissingPluginStatus(WebPluginDelegate* delegate, + int status) { + if (first_default_plugin_ == NULL) { + // Show the InfoBar for the first available plugin. + if (status == default_plugin::MISSING_PLUGIN_AVAILABLE) { + first_default_plugin_ = delegate; + Send(new ViewHostMsg_MissingPluginStatus(routing_id_, status)); + } + } else { + // Closes the InfoBar if user clicks on the plugin (instead of the InfoBar) + // to start the download/install. + if (status == default_plugin::MISSING_PLUGIN_USER_STARTED_DOWNLOAD) { + Send(new ViewHostMsg_MissingPluginStatus(routing_id_, status)); + } + } +} + +void RenderView::OpenURL(WebView* webview, const GURL& url, + WindowOpenDisposition disposition) { + Send(new ViewHostMsg_OpenURL(routing_id_, url, disposition)); +} + +// We are supposed to get a single call to Show for a newly created RenderView +// that was created via RenderView::CreateWebView. So, we wait until this +// point to dispatch the ShowView message. +// +// This method provides us with the information about how to display the newly +// created RenderView (i.e., as a constrained popup or as a new tab). +// +void RenderView::Show(WebWidget* webwidget, WindowOpenDisposition disposition) { + DCHECK(!did_show_) << "received extraneous Show call"; + DCHECK(opener_id_ != MSG_ROUTING_NONE); + + if (did_show_) + return; + did_show_ = true; + + // NOTE: initial_pos_ may still have its default values at this point, but + // that's okay. It'll be ignored if disposition is not NEW_POPUP, or the + // browser process will impose a default position otherwise. + Send(new ViewHostMsg_ShowView( + opener_id_, routing_id_, disposition, initial_pos_, + WasOpenedByUserGestureHelper())); +} + +void RenderView::RunModal(WebWidget* webwidget) { + DCHECK(did_show_) << "should already have shown the view"; + + IPC::SyncMessage* msg = new ViewHostMsg_RunModal(routing_id_); + + msg->set_pump_messages_event(modal_dialog_event_); + Send(msg); +} + +void RenderView::SyncNavigationState() { + if (!webview()) + return; + + GURL url; + std::wstring title; + std::string state; + if (!webview()->GetMainFrame()->GetCurrentState(&url, &title, &state)) + return; + + Send(new ViewHostMsg_UpdateState(routing_id_, page_id_, url, title, state)); +} + +void RenderView::ShowContextMenu(WebView* webview, + ContextNode::Type type, + int x, + int y, + const GURL& link_url, + const GURL& image_url, + const GURL& page_url, + const GURL& frame_url, + const std::wstring& selection_text, + const std::wstring& misspelled_word, + int edit_flags) { + ViewHostMsg_ContextMenu_Params params; + params.type = type; + params.x = x; + params.y = y; + params.image_url = image_url; + params.link_url = link_url; + params.page_url = page_url; + params.frame_url = frame_url; + params.selection_text = selection_text; + params.misspelled_word = misspelled_word; + params.edit_flags = edit_flags; + Send(new ViewHostMsg_ContextMenu(routing_id_, params)); +} + +void RenderView::StartDragging(WebView* webview, const WebDropData& drop_data) { + Send(new ViewHostMsg_StartDragging(routing_id_, drop_data)); +} + +void RenderView::TakeFocus(WebView* webview, bool reverse) { + Send(new ViewHostMsg_TakeFocus(routing_id_, reverse)); +} + +void RenderView::DidDownloadImage(int id, + const GURL& image_url, + bool errored, + const SkBitmap& image) { + Send(new ViewHostMsg_DidDownloadImage(routing_id_, id, image_url, errored, + image)); +} + + +void RenderView::OnDownloadImage(int id, + const GURL& image_url, + int image_size) { + if (!webview()->DownloadImage(id, image_url, image_size)) + Send(new ViewHostMsg_DidDownloadImage(routing_id_, id, image_url, true, + SkBitmap())); +} + +void RenderView::OnGetApplicationInfo(int page_id) { + webkit_glue::WebApplicationInfo app_info; + if (page_id == page_id_) + webkit_glue::GetApplicationInfo(webview(), &app_info); + + // Prune out any data URLs in the set of icons. The browser process expects + // any icon with a data URL to have originated from a favicon. We don't want + // to decode arbitrary data URLs in the browser process. See + // http://b/issue?id=1162972 + for (size_t i = 0; i < app_info.icons.size(); ++i) { + if (app_info.icons[i].url.SchemeIs("data")) { + app_info.icons.erase(app_info.icons.begin() + i); + --i; + } + } + + Send(new ViewHostMsg_DidGetApplicationInfo(routing_id_, page_id, app_info)); +} + +GURL RenderView::GetAlternateErrorPageURL(const GURL& failedURL, + ErrorPageType error_type) { + if (failedURL.SchemeIsSecure()) { + // If the URL that failed was secure, then the embedding web page was not + // expecting a network attacker to be able to manipulate its contents. As + // we fetch alternate error pages over HTTP, we would be allowing a network + // attacker to manipulate the contents of the response if we tried to use + // the link doctor here. + return GURL::EmptyGURL(); + } + + // Grab the base URL from the browser process. + if (!alternate_error_page_url_.is_valid()) + return GURL::EmptyGURL(); + + // Strip query params from the failed URL. + GURL::Replacements remove_params; + remove_params.ClearUsername(); + remove_params.ClearPassword(); + remove_params.ClearQuery(); + remove_params.ClearRef(); + const GURL url_to_send = failedURL.ReplaceComponents(remove_params); + + // Construct the query params to send to link doctor. + std::string params(alternate_error_page_url_.query()); + params.append("&url="); + params.append(EscapeQueryParamValue(url_to_send.spec())); + params.append("&sourceid=chrome"); + params.append("&error="); + switch (error_type) { + case DNS_ERROR: + params.append("dnserror"); + break; + + case HTTP_404: + params.append("http404"); + break; + + default: + NOTREACHED() << "unknown ErrorPageType"; + } + + // OK, build the final url to return. + GURL::Replacements link_doctor_params; + link_doctor_params.SetQueryStr(params); + GURL url = alternate_error_page_url_.ReplaceComponents(link_doctor_params); + return url; +} + +void RenderView::OnFind(const FindInPageRequest& request) { + WebFrame* main_frame = webview()->GetMainFrame(); + WebFrame* frame_after_main = webview()->GetNextFrameAfter(main_frame, true); + WebFrame* focused_frame = webview()->GetFocusedFrame(); + WebFrame* search_frame = focused_frame; // start searching focused frame. + + bool multi_frame = (frame_after_main != main_frame); + + // If we have multiple frames, we don't want to wrap the search within the + // frame, so we check here if we only have main_frame in the chain. + bool wrap_within_frame = !multi_frame; + + gfx::Rect selection_rect; + bool result = false; + + do { + if (request.find_next) + result = search_frame->FindNext(request, wrap_within_frame); + else + result = search_frame->Find(request, wrap_within_frame, &selection_rect); + + if (!result) { + // don't leave text selected as you move to the next frame. + search_frame->ClearSelection(); + + // Find the next frame, but skip the invisible ones. + do { + // What is the next frame to search? (we might be going backwards). Note + // that we specify wrap=true so that search_frame never becomes NULL. + search_frame = request.forward ? + webview()->GetNextFrameAfter(search_frame, true) : + webview()->GetPreviousFrameBefore(search_frame, true); + } while (!search_frame->Visible() && search_frame != focused_frame); + + // make sure selection doesn't affect the search operation in new frame. + search_frame->ClearSelection(); + + // If we have multiple frames and we have wrapped back around to the + // focused frame, we need to search it once more allowing wrap within + // the frame, otherwise it will report 'no match' if the focused frame has + // reported matches, but no frames after the focused_frame contain a + // match for the search word(s). + if (multi_frame && search_frame == focused_frame) { + if (request.find_next) + result = search_frame->FindNext(request, true); // Force wrapping. + else + result = search_frame->Find(request, true, // Force wrapping. + &selection_rect); + } + } + + // TODO(jcampan): http://b/issue?id=1157486 Remove StoreForFocus call once + // we have the fix for 792423. + search_frame->GetView()->StoreFocusForFrame(search_frame); + webview()->SetFocusedFrame(search_frame); + } while (!result && search_frame != focused_frame); + + // Make sure we don't leave any frame focused or the focus won't be restored + // properly in WebViewImpl::SetFocus(). Note that we are talking here about + // focused on the SelectionController, not FocusController. + // webview()->GetFocusedFrame() will still return the last focused frame (as + // it queries the FocusController). + // TODO(jcampan): http://b/issue?id=1157486 Remove next line once we have the + // fix for 792423. + webview()->SetFocusedFrame(NULL); + + // We send back word that we found some matches, because we don't want to lag + // when notifying the user that we found something. At this point we only know + // that we found 1 match, but the scoping effort will tell us more. However, + // if this is a FindNext request, the scoping effort is already under way, or + // done already, so we have partial results. In that case we set it to -1 so + // that it gets ignored by the FindInPageController. + int match_count = result ? 1 : 0; // 1 here means possibly more coming. + if (request.find_next) + match_count = -1; + + // If we find no matches (or if this is Find Next) then this will be our last + // status update. Otherwise the scoping effort will send more results. + bool final_status_update = !result || request.find_next; + + // Send the search result over to the browser process. + Send(new ViewHostMsg_Find_Reply(routing_id_, request.request_id, + match_count, + selection_rect, + -1, // Don't update active match ordinal. + final_status_update)); + + if (!request.find_next) { + // Scoping effort begins, starting with the mainframe. + search_frame = main_frame; + + main_frame->ResetMatchCount(); + + do { + // Cancel all old scoping requests before starting a new one. + search_frame->CancelPendingScopingEffort(); + + // We don't start another scoping effort unless at least one match has + // been found. + if (result) { + // Start new scoping request. If the scoping function determines that it + // needs to scope, it will defer until later. + search_frame->ScopeStringMatches(request, + true); // reset the tickmarks + } + + // Iterate to the next frame. The frame will not necessarily scope, for + // example if it is not visible. + search_frame = webview()->GetNextFrameAfter(search_frame, true); + } while (search_frame != main_frame); + } +} + +void RenderView::ReportFindInPageMatchCount(int count, int request_id, + bool final_update) { + // If we have a message that has been queued up, then we should just replace + // it. The ACK from the browser will make sure it gets sent when the browser + // wants it. + if (queued_find_reply_message_.get()) { + IPC::Message* msg = new ViewHostMsg_Find_Reply( + routing_id_, + request_id, + count, + gfx::Rect(0, 0, 0, 0), + -1, // Don't update active match ordinal. + final_update); + queued_find_reply_message_.reset(msg); + } else { + // Send the search result over to the browser process. + Send(new ViewHostMsg_Find_Reply( + routing_id_, + request_id, + count, + gfx::Rect(0, 0, 0, 0), + -1, // // Don't update active match ordinal. + final_update)); + } +} + +void RenderView::ReportFindInPageSelection(int request_id, + int active_match_ordinal, + const gfx::Rect& selection_rect) { + // Send the search result over to the browser process. + Send(new ViewHostMsg_Find_Reply(routing_id_, + request_id, + -1, + selection_rect, + active_match_ordinal, + false)); +} + +bool RenderView::WasOpenedByUserGesture(WebView* webview) const { + return WasOpenedByUserGestureHelper(); +} + +bool RenderView::WasOpenedByUserGestureHelper() const { + // If pop-up blocking has been disabled, then treat all new windows as if + // they were opened by a user gesture. This will prevent them from being + // blocked. This is a bit of a hack, there should be a more straightforward + // way to disable pop-up blocking. + if (disable_popup_blocking_) + return true; + + return opened_by_user_gesture_; +} + +void RenderView::SpellCheck(const std::wstring& word, int& misspell_location, + int& misspell_length) { + Send(new ViewHostMsg_SpellCheck(routing_id_, word, &misspell_location, + &misspell_length)); +} + +void RenderView::SetInputMethodState(bool enabled) { + // Save the updated IME status and mark the input focus has been updated. + // The IME status is to be sent to a browser process next time when + // the input caret is rendered. + ime_control_updated_ = true; + ime_control_new_state_ = enabled; +} + +void RenderView::ScriptedPrint(WebFrame* frame) { + // Retrieve the default print settings to calculate the expected number of + // pages. + ViewMsg_Print_Params default_settings; + IPC::SyncMessage* msg = + new ViewHostMsg_GetDefaultPrintSettings(routing_id_, &default_settings); + if (Send(msg)) { + msg = NULL; + // Continue only if the settings are valid. + if (default_settings.dpi && default_settings.document_cookie) { + int expected_pages_count = SwitchFrameToPrintMediaType(default_settings, + frame); + DCHECK(expected_pages_count); + SwitchFrameToDisplayMediaType(frame); + + // Ask the browser to show UI to retrieve the final print settings. + ViewMsg_PrintPages_Params print_settings; + // host_window_ may be NULL at this point if the current window is a popup + // and the print() command has been issued from the parent. The receiver + // of this message has to deal with this. + msg = new ViewHostMsg_ScriptedPrint(routing_id_, + host_window_, + default_settings.document_cookie, + expected_pages_count, + &print_settings); + if (Send(msg)) { + msg = NULL; + + // If the settings are invalid, early quit. + if (print_settings.params.dpi && + print_settings.params.document_cookie) { + // Render the printed pages. It will implicitly revert the document to + // display CSS media type. + PrintPages(print_settings, frame); + // All went well. + return; + } else { + // The user cancelled. + } + } else { + // Send() failed. + NOTREACHED(); + } + } else { + // The user cancelled. + } + } else { + // Send() failed. + NOTREACHED(); + } + // TODO(maruel): bug 1123882 Alert the user that printing failed. +} + +void RenderView::WebInspectorOpened(int num_resources) { + Send(new ViewHostMsg_InspectElement_Reply(routing_id_, num_resources)); +} + +void RenderView::UserMetricsRecordAction(const std::wstring& action) { + Send(new ViewHostMsg_UserMetricsRecordAction(routing_id_, action)); +} + +void RenderView::DnsPrefetch(const std::vector<std::string>& host_names) { + Send(new ViewHostMsg_DnsPrefetch(host_names)); +} + +void RenderView::OnAlterTextSize(int size) { + switch (size) { + case text_zoom::TEXT_SMALLER: + webview()->MakeTextSmaller(); + break; + case text_zoom::TEXT_STANDARD: + webview()->MakeTextStandardSize(); + break; + case text_zoom::TEXT_LARGER: + webview()->MakeTextLarger(); + break; + default: + NOTREACHED(); + } +} + +void RenderView::OnSetPageEncoding(const std::wstring& encoding_name) { + webview()->SetPageEncoding(encoding_name); +} + +void RenderView::OnPasswordFormsSeen(WebView* webview, + const std::vector<PasswordForm>& forms) { + Send(new ViewHostMsg_PasswordFormsSeen(routing_id_, forms)); +} + +WebHistoryItem* RenderView::GetHistoryEntryAtOffset(int offset) { + // This doesn't work in the multi-process case because we don't want to + // hang, as it might lead to deadlocks. Use GoToEntryAtOffsetAsync. + return NULL; +} + +void RenderView::GoToEntryAtOffsetAsync(int offset) { + Send(new ViewHostMsg_GoToEntryAtOffset(routing_id_, offset)); +} + +int RenderView::GetHistoryBackListCount() { + return history_back_list_count_; +} + +int RenderView::GetHistoryForwardListCount() { + return history_forward_list_count_; +} + +void RenderView::OnNavStateChanged(WebView* webview) { + nav_state_sync_timer_.Start(); +} + +void RenderView::SetTooltipText(WebView* webview, + const std::wstring& tooltip_text) { + Send(new ViewHostMsg_SetTooltipText(routing_id_, tooltip_text)); +} + +void RenderView::DownloadUrl(const GURL& url, const GURL& referrer) { + Send(new ViewHostMsg_DownloadUrl(routing_id_, url, referrer)); +} + +WebFrame* RenderView::GetChildFrame(const std::wstring& frame_xpath) const { + WebFrame* web_frame; + if (frame_xpath.empty()) { + web_frame = webview()->GetMainFrame(); + } else { + web_frame = webview()->GetMainFrame()->GetChildFrame(frame_xpath); + } + + return web_frame; +} + +void RenderView::EvaluateScriptUrl(const std::wstring& frame_xpath, + const std::wstring& js_url) { + WebFrame* web_frame = GetChildFrame(frame_xpath); + if (!web_frame) + return; + + scoped_ptr<WebRequest> request(WebRequest::Create(GURL(js_url))); + web_frame->LoadRequest(request.get()); +} + +void RenderView::OnScriptEvalRequest(const std::wstring& frame_xpath, + const std::wstring& jscript) { + EvaluateScriptUrl(frame_xpath, jscript); +} + +void RenderView::OnAddMessageToConsole(const std::wstring& frame_xpath, + const std::wstring& msg, + ConsoleMessageLevel level) { + WebFrame* web_frame = GetChildFrame(frame_xpath); + if (!web_frame) + return; + + web_frame->AddMessageToConsole(msg, level); +} + +void RenderView::OnDebugAttach() { + EvaluateScriptUrl(L"", L"javascript:void(0)"); + Send(new ViewHostMsg_DidDebugAttach(routing_id_)); + // Tell the plugin host to stop accepting messages in order to avoid + // hangs while the renderer is paused. + // TODO(1243929): It might be an improvement to add more plumbing to do this + // when the renderer is actually paused vs. just the debugger being attached. + PluginChannelHost::SetListening(false); +} + +void RenderView::OnDebugDetach() { + // Tell the plugin host to start accepting plugin messages again. + PluginChannelHost::SetListening(true); +} + +void RenderView::OnAllowDomAutomationBindings(bool allow_bindings) { + enable_dom_automation_ = allow_bindings; +} + +void RenderView::OnAllowDOMUIBindings() { + enable_dom_ui_bindings_ = true; +} + +void RenderView::OnSetDOMUIProperty(const std::string& name, + const std::string& value) { + DCHECK(enable_dom_ui_bindings_); + dom_ui_bindings_.SetProperty(name, value); +} + +void RenderView::OnReservePageIDRange(int size_of_range) { + next_page_id_ += size_of_range + 1; +} + +void RenderView::OnDragSourceEndedOrMoved(int client_x, + int client_y, + int screen_x, + int screen_y, + bool ended) { + if (ended) + webview()->DragSourceEndedAt(client_x, client_y, screen_x, screen_y); + else + webview()->DragSourceMovedTo(client_x, client_y, screen_x, screen_y); +} + +void RenderView::OnDragSourceSystemDragEnded() { + webview()->DragSourceSystemDragEnded(); +} + +void RenderView::OnUploadFileRequest(const ViewMsg_UploadFile_Params& p) { + webkit_glue::FileUploadData* f = new webkit_glue::FileUploadData; + f->file_path = p.file_path; + f->form_name = p.form; + f->file_name = p.file; + f->submit_name = p.submit; + + // Build the other form values map. + if (!p.other_values.empty()) { + std::vector<std::wstring> e; + std::vector<std::wstring> kvp; + std::vector<std::wstring>::iterator i; + + SplitString(p.other_values, L'\n', &e); + for (i = e.begin(); i != e.end(); ++i) { + SplitString(*i, L'=', &kvp); + if (kvp.size() == 2) + f->other_form_values[kvp[0]] = kvp[1]; + kvp.clear(); + } + } + + pending_upload_data_.reset(f); + ProcessPendingUpload(); +} + +void RenderView::ProcessPendingUpload() { + webkit_glue::FileUploadData* f = pending_upload_data_.get(); + if (f && webview() && webkit_glue::FillFormToUploadFile(webview(), *f)) + ResetPendingUpload(); +} + +void RenderView::ResetPendingUpload() { + pending_upload_data_.reset(); +} + +void RenderView::OnFormFill(const FormData& form) { + webkit_glue::FillForm(this->webview(), form); +} + +void RenderView::OnFillPasswordForm( + const PasswordFormDomManager::FillData& form_data) { + webkit_glue::FillPasswordForm(this->webview(), form_data); +} + +void RenderView::OnDragTargetDragEnter(const WebDropData& drop_data, + const gfx::Point& client_pt, const gfx::Point& screen_pt) { + bool is_drop_target = webview()->DragTargetDragEnter(drop_data, + client_pt.x(), client_pt.y(), screen_pt.x(), screen_pt.y()); + + Send(new ViewHostMsg_UpdateDragCursor(routing_id_, is_drop_target)); +} + +void RenderView::OnDragTargetDragOver(const gfx::Point& client_pt, + const gfx::Point& screen_pt) { + bool is_drop_target = webview()->DragTargetDragOver(client_pt.x(), + client_pt.y(), screen_pt.x(), screen_pt.y()); + + Send(new ViewHostMsg_UpdateDragCursor(routing_id_, is_drop_target)); +} + +void RenderView::OnDragTargetDragLeave() { + webview()->DragTargetDragLeave(); +} + +void RenderView::OnDragTargetDrop(const gfx::Point& client_pt, + const gfx::Point& screen_pt) { + webview()->DragTargetDrop(client_pt.x(), client_pt.y(), screen_pt.x(), + screen_pt.y()); +} + +void RenderView::OnUpdateWebPreferences(const WebPreferences& prefs) { + webview()->SetPreferences(prefs); +} + +void RenderView::OnSetAltErrorPageURL(const GURL& url) { + alternate_error_page_url_ = url; +} + +void RenderView::DidPaint() { + PluginDelegateList::iterator it = plugin_delegates_.begin(); + while (it != plugin_delegates_.end()) { + (*it)->FlushGeometryUpdates(); + ++it; + } +} + +void RenderView::OnInstallMissingPlugin() { + // This could happen when the first default plugin is deleted. + if (first_default_plugin_ == NULL) + return; + first_default_plugin_->InstallMissingPlugin(); +} + +void RenderView::OnFileChooserResponse(const std::wstring& file_name) { + file_chooser_->OnFileChoose(file_name); + file_chooser_.reset(); +} + +void RenderView::OnEnableViewSourceMode() { + if (!webview()) + return; + WebFrame* main_frame = webview()->GetMainFrame(); + if (!main_frame) + return; + + main_frame->SetInViewSourceMode(true); +} + +void RenderView::OnUpdateBackForwardListCount(int back_list_count, + int forward_list_count) { + history_back_list_count_ = back_list_count; + history_forward_list_count_ = forward_list_count; +} + +void RenderView::OnGetAllSavableResourceLinksForCurrentPage( + const GURL& page_url) { + // Prepare list to storage all savable resource links. + std::vector<GURL> resources_list; + std::vector<GURL> referrers_list; + std::vector<GURL> frames_list; + webkit_glue::SavableResourcesResult result(&resources_list, + &referrers_list, + &frames_list); + + if (!webkit_glue::GetAllSavableResourceLinksForCurrentPage(webview(), + page_url, + &result)) { + // If something is wrong when collecting all savable resource links, + // send empty list to embedder(browser) to tell it failed. + referrers_list.clear(); + resources_list.clear(); + frames_list.clear(); + } + + // Send result of all savable resource links to embedder. + Send(new ViewHostMsg_SendCurrentPageAllSavableResourceLinks(routing_id_, + resources_list, + referrers_list, + frames_list)); +} + +void RenderView::OnGetSerializedHtmlDataForCurrentPageWithLocalLinks( + const std::vector<std::wstring>& links, + const std::vector<std::wstring>& local_paths, + const std::wstring& local_directory_name) { + webkit_glue::DomSerializer dom_serializer(webview()->GetMainFrame(), + true, + this, + links, + local_paths, + local_directory_name); + dom_serializer.SerializeDom(); +} + +void RenderView::DidSerializeDataForFrame(const GURL& frame_url, + const std::string& data, PageSavingSerializationStatus status) { + Send(new ViewHostMsg_SendSerializedHtmlData(routing_id_, + frame_url, data, static_cast<int32>(status))); +} + +void RenderView::OnMsgShouldClose(bool is_closing_browser) { + bool should_close = webview()->ShouldClose(); + + Send(new ViewHostMsg_ShouldClose_ACK(routing_id_, should_close, + is_closing_browser)); +} + +void RenderView::OnClosePage(int new_render_process_host_id, + int new_request_id, + bool is_closing_browser) { + // TODO(creis): We'd rather use webview()->Close() here, but that currently + // sets the WebView's delegate_ to NULL, preventing any JavaScript dialogs + // in the onunload handler from appearing. For now, we're bypassing that and + // calling the FrameLoader's CloseURL method directly. This should be + // revisited to avoid having two ways to close a page. Having a single way + // to close that can run onunload is also useful for fixing + // http://b/issue?id=753080. + WebFrame* main_frame = webview()->GetMainFrame(); + if (main_frame) + main_frame->ClosePage(); + + Send(new ViewHostMsg_ClosePage_ACK(routing_id_, + new_render_process_host_id, + new_request_id, + is_closing_browser)); +} + +void RenderView::OnThemeChanged() { + gfx::NativeTheme::instance()->CloseHandles(); + gfx::Rect view_rect(0, 0, size_.width(), size_.height()); + DidInvalidateRect(webwidget_, view_rect); +} + +std::string RenderView::GetAltHTMLForTemplate( + const DictionaryValue& error_strings, int template_resource_id) const { + const StringPiece template_html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + template_resource_id)); + + if (template_html.empty()) { + NOTREACHED() << "unable to load template. ID: " << template_resource_id; + return ""; + } + // "t" is the id of the templates root node. + return jstemplate_builder::GetTemplateHtml( + template_html, &error_strings, "t"); +} diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h new file mode 100644 index 0000000..844d877 --- /dev/null +++ b/chrome/renderer/render_view.h @@ -0,0 +1,623 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_RENDERER_RENDER_VIEW_H__ +#define CHROME_RENDERER_RENDER_VIEW_H__ + +#include <hash_map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/scoped_handle.h" +#include "base/gfx/point.h" +#include "base/gfx/rect.h" +#include "base/timer.h" +#include "base/values.h" +#include "chrome/common/resource_dispatcher.h" +#include "chrome/renderer/automation/dom_automation_controller.h" +#include "chrome/renderer/dom_ui_bindings.h" +#include "chrome/renderer/external_js_object.h" +#include "chrome/renderer/render_process.h" +#include "chrome/renderer/render_widget.h" +#include "webkit/glue/console_message_level.h" +#include "webkit/glue/dom_serializer_delegate.h" +#include "webkit/glue/webview_delegate.h" +#include "webkit/glue/webview.h" + +// RenderView is a diamond-shaped hierarchy, with WebWidgetDelegate at the root. +// VS warns when we inherit the WebWidgetDelegate method implementations from +// RenderWidget. It's safe to ignore that warning. +#pragma warning(disable: 4250) + +class DebugMessageHandler; +class GURL; +class SharedMemory; +class SkBitmap; +struct ThumbnailScore; +class WebError; +class WebFrame; +class WebPluginDelegate; +class WebPluginDelegateProxy; +enum WebRequestCachePolicy; + +namespace webkit_glue { + struct FileUploadData; +} + +// +// RenderView is an object that manages a WebView object, and provides a +// communication interface with an embedding application process +// +class RenderView : public RenderWidget, public WebViewDelegate, + public webkit_glue::DomSerializerDelegate { + public: + // Creates a new RenderView. The parent_hwnd specifies a HWND to use as the + // parent of the WebView HWND that will be created. The modal_dialog_event + // is set by the RenderView whenever a modal dialog alert is shown, so that + // the renderer and plugin processes know to pump window messages. If this + // is a constrained popup or as a new tab, opener_id is the routing ID of the + // RenderView responsible for creating this RenderView (corresponding to the + // parent_hwnd). + static RenderView* Create(HWND parent_hwnd, + HANDLE modal_dialog_event, + int32 opener_id, + const WebPreferences& webkit_prefs, + int32 routing_id); + + // Sets the "next page id" counter. + static void SetNextPageID(int32 next_page_id); + + // The resource dispatcher used to fetch resources for this view. + ResourceDispatcher* resource_dispatcher() { + return resource_dispatcher_; + } + + // May return NULL when the view is closing. + WebView* webview() const { + return static_cast<WebView*>(webwidget()); + } + + HWND host_window() const { + return host_window_; + } + + HANDLE modal_dialog_event() { + return modal_dialog_event_.Get(); + } + + // IPC::Channel::Listener + virtual void OnMessageReceived(const IPC::Message& msg); + + // WebViewDelegate + virtual void RunJavaScriptAlert(WebView* webview, + const std::wstring& message); + virtual bool RunJavaScriptConfirm(WebView* webview, + const std::wstring& message); + virtual bool RunJavaScriptPrompt(WebView* webview, + const std::wstring& message, + const std::wstring& default_value, + std::wstring* result); + virtual bool RunBeforeUnloadConfirm(WebView* webview, + const std::wstring& message); + virtual void OnUnloadListenerChanged(WebView* webview, WebFrame* webframe); + virtual void UpdateTargetURL(WebView* webview, + const GURL& url); + virtual void RunFileChooser(const std::wstring& default_filename, + WebFileChooserCallback* file_chooser); + virtual void AddMessageToConsole(WebView* webview, + const std::wstring& message, + unsigned int line_no, + const std::wstring& source_id); + + virtual void DebuggerOutput(const std::wstring& out); + + virtual void DidStartLoading(WebView* webview); + virtual void DidStopLoading(WebView* webview); + virtual void DidStartProvisionalLoadForFrame( + WebView* webview, + WebFrame* frame, + NavigationGesture gesture); + virtual void DidReceiveProvisionalLoadServerRedirect(WebView* webview, + WebFrame* frame); + virtual void DidFailProvisionalLoadWithError(WebView* webview, + const WebError& error, + WebFrame* frame); + virtual void LoadNavigationErrorPage(WebFrame* frame, + const WebRequest* failed_request, + const WebError& error, + const std::string& html, + bool replace); + virtual void DidCommitLoadForFrame(WebView* webview, WebFrame* frame, + bool is_new_navigation); + virtual void DidReceiveTitle(WebView* webview, + const std::wstring& title, + WebFrame* frame); + virtual void DidFinishLoadForFrame(WebView* webview, + WebFrame* frame); + virtual void DidFailLoadWithError(WebView* webview, + const WebError& error, + WebFrame* forFrame); + virtual void DidFinishDocumentLoadForFrame(WebView* webview, WebFrame* frame); + virtual bool DidLoadResourceFromMemoryCache(WebView* webview, + const WebRequest& request, + const WebResponse& response, + WebFrame* frame); + virtual void DidHandleOnloadEventsForFrame(WebView* webview, WebFrame* frame); + virtual void DidChangeLocationWithinPageForFrame(WebView* webview, + WebFrame* frame, + bool is_new_navigation); + virtual void DidReceiveIconForFrame(WebView* webview, WebFrame* frame); + + virtual void WillPerformClientRedirect(WebView* webview, + WebFrame* frame, + const GURL& src_url, + const GURL& dest_url, + unsigned int delay_seconds, + unsigned int fire_date); + virtual void DidCancelClientRedirect(WebView* webview, + WebFrame* frame); + virtual void DidCompleteClientRedirect(WebView* webview, + WebFrame* frame, + const GURL& source); + + virtual void WindowObjectCleared(WebFrame* webframe); + virtual WindowOpenDisposition DispositionForNavigationAction( + WebView* webview, + WebFrame* frame, + const WebRequest* request, + WebNavigationType type, + WindowOpenDisposition disposition, + bool is_redirect); + + virtual WebView* CreateWebView(WebView* webview, bool user_gesture); + virtual WebWidget* CreatePopupWidget(WebView* webview); + virtual WebPluginDelegate* CreatePluginDelegate( + WebView* webview, + const GURL& url, + const std::string& mime_type, + const std::string& clsid, + std::string* actual_mime_type); + virtual void OnMissingPluginStatus(WebPluginDelegate* delegate, int status); + virtual void OpenURL(WebView* webview, const GURL& url, + WindowOpenDisposition disposition); + virtual void DidDownloadImage(int id, + const GURL& image_url, + bool errored, + const SkBitmap& image); + virtual GURL GetAlternateErrorPageURL(const GURL& failedURL, + ErrorPageType error_type); + + virtual void ShowContextMenu(WebView* webview, + ContextNode::Type type, + int x, + int y, + const GURL& link_url, + const GURL& image_url, + const GURL& page_url, + const GURL& frame_url, + const std::wstring& selection_text, + const std::wstring& misspelled_word, + int edit_flags); + virtual void StartDragging(WebView* webview, + const WebDropData& drag_data); + + virtual void TakeFocus(WebView* webview, bool reverse); + virtual void JSOutOfMemory(); + + virtual WebHistoryItem* GetHistoryEntryAtOffset(int offset); + virtual void GoToEntryAtOffsetAsync(int offset); + virtual int GetHistoryBackListCount(); + virtual int GetHistoryForwardListCount(); + virtual void OnNavStateChanged(WebView* webview); + virtual void SetTooltipText(WebView* webview, + const std::wstring& tooltip_text); + + virtual void DownloadUrl(const GURL& url, const GURL& referrer); + + virtual void OnPasswordFormsSeen(WebView* webview, + const std::vector<PasswordForm>& forms); + + virtual void ReportFindInPageMatchCount(int count, int request_id, + bool final_update); + virtual void ReportFindInPageSelection(int request_id, + int active_match_ordinal, + const gfx::Rect& selection_rect); + virtual bool WasOpenedByUserGesture(WebView* webview) const; + virtual void SpellCheck(const std::wstring& word, int& misspell_location, + int& misspell_length); + virtual void SetInputMethodState(bool enabled); + virtual void ScriptedPrint(WebFrame* frame); + virtual void WebInspectorOpened(int num_resources); + virtual void UserMetricsRecordAction(const std::wstring& action); + virtual void DnsPrefetch(const std::vector<std::string>& host_names); + + // DomSerializerDelegate + virtual void DidSerializeDataForFrame(const GURL& frame_url, + const std::string& data, PageSavingSerializationStatus status); + + // WebWidgetDelegate + // Most methods are handled by RenderWidget. + virtual void Show(WebWidget* webwidget, WindowOpenDisposition disposition); + virtual void RunModal(WebWidget* webwidget); + + // Do not delete directly. This class is reference counted. + virtual ~RenderView(); + + // Called when a plugin is destroyed. + void PluginDestroyed(WebPluginDelegateProxy* proxy); + + // Called when a plugin is crashed. + void PluginCrashed(const std::wstring& plugin_path); + + // Shows a modal HTML dialog. + void ShowModalHTMLDialog(const GURL& url, int width, int height, + const std::string& json_arguments, + std::string* json_retval); + + // Called from JavaScript window.external.AddSearchProvider() to add a + // keyword for a provider described in the given OpenSearch document. + void AddSearchProvider(const std::string& url); + + // Asks the browser for the CPBrowsingContext associated with this renderer. + uint32 GetCPBrowsingContext(); + + // Dispatches the current navigation state to the browser. Called on a + // periodic timer so we don't send too many messages. + void SyncNavigationState(); + + // Evaluates a javascript: URL + void EvaluateScriptUrl(const std::wstring& frame_xpath, + const std::wstring& jscript); + + // Called when the Javascript debugger is no longer attached. + // This is called from within the renderer, not via an IPC message. + void OnDebugDetach(); + + private: + RenderView(); + + // When we are created from window.open from an already existing view, this + // constructor stores that view ID. + explicit RenderView(int32 opener_id); + + // Initializes this view with the given parent and ID. The |routing_id| can be + // set to 'MSG_ROUTING_NONE' if the true ID is not yet known. In this case, + // CompleteInit must be called later with the true ID. + void Init(HWND parent, + HANDLE modal_dialog_event, + int32 opener_id, + const WebPreferences& webkit_prefs, + int32 routing_id); + + void UpdateURL(WebFrame* frame); + void UpdateTitle(WebFrame* frame, const std::wstring& title); + void UpdateSessionHistory(WebFrame* frame); + + // Update current main frame's encoding and send it to browser window. + // Since we want to let users see the right encoding info from menu + // before finishing loading, we call the UpdateEncoding in + // a) function:DidCommitLoadForFrame. When this function is called, + // that means we have got first data. In here we try to get encoding + // of page if it has been specified in http header. + // b) function:DidReceiveTitle. When this function is called, + // that means we have got specified title. Because in most of webpages, + // title tags will follow meta tags. In here we try to get encoding of + // page if it has been specified in meta tag. + // c) function:DidFinishDocumentLoadForFrame. When this function is + // called, that means we have got whole html page. In here we should + // finally get right encoding of page. + void UpdateEncoding(WebFrame* frame, const std::wstring& encoding_name); + + // Captures the thumbnail and text contents for indexing for the given load + // ID. If the view's load ID is different than the parameter, this call is + // a NOP. Typically called on a timer, so the load ID may have changed in the + // meantime. + void CapturePageInfo(int load_id, bool preliminary_capture); + + // Called to retrieve the text from the given frame contents, the page text + // up to the maximum amount will be placed into the given buffer + void CaptureText(WebFrame* frame, std::wstring* contents); + + // Creates a thumbnail of |frame|'s contents resized to (|w|, |h|) + // and puts that in |thumbnail|. Thumbnail metadata goes in |score|. + void CaptureThumbnail(WebFrame* frame, int w, int h, + SkBitmap* thumbnail, + ThumbnailScore* score); + + // Calculates how "boring" a thumbnail is. The boring score is the + // 0,1 ranged percentage of pixels that are the most common + // luma. Higher boring scores indicate that a higher percentage of a + // bitmap are all the same brightness. + double CalculateBoringScore(SkBitmap* bitmap); + + bool RunJavaScriptMessage(int type, + const std::wstring& message, + const std::wstring& default_value, + std::wstring* result); + + // Adds search provider from the given OpenSearch description URL as a + // keyword search. + void AddGURLSearchProvider(const GURL& osd_url, bool autodetected); + + // RenderView IPC message handlers + void OnCreatingNewAck(HWND parent); + void SendThumbnail(); + void OnPrintPage(const ViewMsg_PrintPage_Params& params); + void OnGetPrintedPagesCount(const ViewMsg_Print_Params& params); + void OnPrintPages(const ViewMsg_PrintPages_Params& params); + void OnNavigate(const ViewMsg_Navigate_Params& params); + void OnStop(); + void OnLoadAlternateHTMLText(const std::string& html_contents, + bool new_navigation, + const GURL& display_url, + const std::string& security_info); + void OnStopFinding(bool clear_selection); + void OnFindReplyAck(); + void OnUpdateTargetURLAck(); + void OnUndo(); + void OnRedo(); + void OnCut(); + void OnCopy(); + void OnPaste(); + void OnReplace(const std::wstring& text); + void OnDelete(); + void OnSelectAll(); + void OnCopyImageAt(int x, int y); + void OnInspectElement(int x, int y); + void OnShowJavaScriptConsole(); + void OnCancelDownload(int32 download_id); + void OnFind(const FindInPageRequest& request); + void OnAlterTextSize(int size); + void OnSetPageEncoding(const std::wstring& encoding_name); + void OnGetAllSavableResourceLinksForCurrentPage(const GURL& page_url); + void OnGetSerializedHtmlDataForCurrentPageWithLocalLinks( + const std::vector<std::wstring>& links, + const std::vector<std::wstring>& local_paths, + const std::wstring& local_directory_name); + void OnUploadFileRequest(const ViewMsg_UploadFile_Params& p); + void OnFormFill(const FormData& form); + void OnFillPasswordForm(const PasswordFormDomManager::FillData& form_data); + void OnDragTargetDragEnter(const WebDropData& drop_data, + const gfx::Point& client_pt, + const gfx::Point& screen_pt); + void OnDragTargetDragOver(const gfx::Point& client_pt, + const gfx::Point& screen_pt); + void OnDragTargetDragLeave(); + void OnDragTargetDrop(const gfx::Point& client_pt, + const gfx::Point& screen_pt); + void OnAllowDomAutomationBindings(bool allow_binding); + void OnAllowDOMUIBindings(); + void OnSetDOMUIProperty(const std::string& name, const std::string& value); + void OnSetInitialFocus(bool reverse); + void OnUpdateWebPreferences(const WebPreferences& prefs); + void OnSetAltErrorPageURL(const GURL& gurl); + + void OnDownloadImage(int id, const GURL& image_url, int image_size); + + void OnGetApplicationInfo(int page_id); + + void OnScriptEvalRequest(const std::wstring& frame_xpath, + const std::wstring& jscript); + void OnAddMessageToConsole(const std::wstring& frame_xpath, + const std::wstring& msg, + ConsoleMessageLevel level); + void OnDebugAttach(); + + void OnReservePageIDRange(int size_of_range); + + void OnDragSourceEndedOrMoved( + int client_x, int client_y, int screen_x, int screen_y, bool ended); + void OnDragSourceSystemDragEnded(); + void OnInstallMissingPlugin(); + void OnFileChooserResponse(const std::wstring& file_name); + void OnEnableViewSourceMode(); + void OnUpdateBackForwardListCount(int back_list_count, + int forward_list_count); + + // Checks if the RenderView should close, runs the beforeunload handler and + // sends ViewMsg_ShouldClose to the browser. + void OnMsgShouldClose(bool is_closing_browser); + + // Runs the onunload handler and closes the page, replying with ClosePage_ACK + // (with the given RPH and request IDs, to help track the request). + void OnClosePage(int new_render_process_host_id, int new_request_id, + bool is_closing_browser); + + // Notification about ui theme changes. + void OnThemeChanged(); + + // Switches the frame's CSS media type to "print" and calculate the number of + // printed pages that are to be expected. |frame| will be used to calculate + // the number of expected pages for this frame only. + int SwitchFrameToPrintMediaType(const ViewMsg_Print_Params& params, + WebFrame* frame); + + // Switches the frame's CSS media type to "display". + void SwitchFrameToDisplayMediaType(WebFrame* frame); + + // Prints the page listed in |params|. + void PrintPage(const ViewMsg_PrintPage_Params& params, WebFrame* frame); + + // Prints all the pages listed in |params|. + void PrintPages(const ViewMsg_PrintPages_Params& params, WebFrame* frame); + + // Attempt to upload the file that we are trying to process if any. + // Reset the pending file upload data if the form was successfully + // posted. + void ProcessPendingUpload(); + + // Reset the pending file upload. + void ResetPendingUpload(); + + // Exposes the DOMAutomationController object that allows JS to send + // information to the browser process. + void BindDOMAutomationController(WebFrame* webframe); + + void set_opened_by_user_gesture(bool value) { + opened_by_user_gesture_ = value; + } + + // Called by RenderWidget after it paints. + virtual void DidPaint(); + + // Locates a sub frame with given xpath + WebFrame* GetChildFrame(const std::wstring& frame_xpath) const; + + std::string GetAltHTMLForTemplate(const DictionaryValue& error_strings, + int template_resource_id) const; + + // A helper method used by WasOpenedByUserGesture. + bool WasOpenedByUserGestureHelper() const; + + // Handles resource loads for this view. + scoped_refptr<ResourceDispatcher> resource_dispatcher_; + + // DOM Automation Controller CppBoundClass + bool enable_dom_automation_; + DomAutomationController dom_automation_controller_; + + // Chrome page<->browser messaging CppBoundClass + bool enable_dom_ui_bindings_; + DOMUIBindings dom_ui_bindings_; + + // window.external object for "built-in" JS extensions + ExternalJSObject external_js_object_; + + // The last gotten main frame's encoding. + std::wstring last_encoding_name_; + + // The URL we think the user's mouse is hovering over. We use this to + // determine if we want to send a new one (we do not need to send + // duplicates). + GURL target_url_; + + // The state of our target_url transmissions. When we receive a request to + // send a URL to the browser, we set this to TARGET_INFLIGHT until an ACK + // comes back - if a new request comes in before the ACK, we store the new + // URL in pending_target_url_ and set the status to TARGET_PENDING. If an + // ACK comes back and we are in TARGET_PENDING, we send the stored URL and + // revert to TARGET_INFLIGHT. + // + // We don't need a queue of URLs to send, as only the latest is useful. + enum { + TARGET_NONE, + TARGET_INFLIGHT, // We have a request in-flight, waiting for an ACK + TARGET_PENDING // INFLIGHT + we have a URL waiting to be sent + } target_url_status_; + + // The next target URL we want to send to the browser. + GURL pending_target_url_; + + // Are we loading our top level frame + bool is_loading_; + + // If we are handling a top-level client-side redirect, this tracks the URL + // of the page that initiated it. Specifically, when a load is committed this + // is used to determine if that load originated from a client-side redirect. + // It is empty if there is no top-level client-side redirect. + GURL completed_client_redirect_src_; + + // The gesture that initiated the current navigation. + NavigationGesture navigation_gesture_; + + // Unique id to identify the current page between browser and renderer. + // + // Note that this is NOT updated for every main frame navigation, only for + // "regular" navigations that go into session history. In particular, client + // redirects, like the page cycler uses (document.location.href="foo") do not + // count as regular navigations and do not increment the page id. + int32 page_id_; + + // Indicates the ID of the last page that we sent a FrameNavigate to the + // browser for. This is used to determine if the most recent transition + // generated a history entry (less than page_id_), or not (equal to or + // greater than). Note that this will be greater than page_id_ if the user + // goes back. + int32 last_page_id_sent_to_browser_; + + // Page_id from the last page we indexed. This prevents us from indexing the + // same page twice in a row. + int32 last_indexed_page_id_; + + // Used for popups. + bool opened_by_user_gesture_; + + // The alternate error page URL, if one exists. + GURL alternate_error_page_url_; + + // The pending file upload. + scoped_ptr<webkit_glue::FileUploadData> pending_upload_data_; + + ScopedRunnableMethodFactory<RenderView> method_factory_; + + // Timer used to delay the updating of nav state (see SyncNavigationState). + OneShotTimer nav_state_sync_timer_; + + typedef std::vector<WebPluginDelegateProxy*> PluginDelegateList; + PluginDelegateList plugin_delegates_; + + // Remember the first uninstalled plugin, so that we can ask the plugin + // to install itself when user clicks on the info bar. + WebPluginDelegate* first_default_plugin_; + + // If the browser hasn't sent us an ACK for the last FindReply we sent + // to it, then we need to queue up the message (keeping only the most + // recent message if new ones come in). + scoped_ptr<IPC::Message> queued_find_reply_message_; + + // Handle to an event that's set when the page is showing a modal dialog (or + // equivalent constrained window). The renderer and any plugin processes + // check this to know if they should pump messages/tasks then. + ScopedHandle modal_dialog_event_; + + // Document width when in print CSS media type. 0 otherwise. + int printed_document_width_; + + // Backup the view size before printing since it needs to be overriden. This + // value is set to restore the view size when printing is done. + gfx::Size printing_view_size_; + + scoped_refptr<DebugMessageHandler> debug_message_handler_; + + scoped_ptr<WebFileChooserCallback> file_chooser_; + + int history_back_list_count_; + int history_forward_list_count_; + + // True if pop-up blocking is disabled. False by default. + bool disable_popup_blocking_; + + // True if the page has any frame-level unload or beforeunload listeners. + bool has_unload_listener_; + + DISALLOW_EVIL_CONSTRUCTORS(RenderView); +}; + +#endif // CHROME_RENDERER_RENDER_VIEW_H__ diff --git a/chrome/renderer/render_widget.cc b/chrome/renderer/render_widget.cc new file mode 100644 index 0000000..c5db805 --- /dev/null +++ b/chrome/renderer/render_widget.cc @@ -0,0 +1,769 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/renderer/render_widget.h" + +#include <windows.h> + +#include "base/gfx/point.h" +#include "base/gfx/size.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/gfx/platform_canvas.h" +#include "base/scoped_ptr.h" +#include "webkit/glue/webinputevent.h" +#include "webkit/glue/webwidget.h" + +/////////////////////////////////////////////////////////////////////////////// + +namespace { + +// This class is used to defer calling RenderWidget::Close() while the current +// thread is inside RenderThread::Send(), which in some cases can result in a +// nested MessageLoop being run. +class DeferredCloses : public Task { + public: + // Called to queue a deferred close for the given widget. + static void Push(RenderWidget* widget) { + if (!current_) + current_ = new DeferredCloses(); + current_->queue_.push(widget); + } + + // Called to trigger any deferred closes to be run. + static void Post() { + DCHECK(!RenderThread::current()->in_send()); + if (current_) { + MessageLoop::current()->PostTask(FROM_HERE, current_); + current_ = NULL; + } + } + + private: + virtual void Run() { + // Maybe we are being run from within another RenderWidget::Send call. If + // that is true, then we need to re-queue the widgets to be closed and try + // again later. + while (!queue_.empty()) { + if (RenderThread::current()->in_send()) { + Push(queue_.front()); + } else { + queue_.front()->Close(); + } + queue_.pop(); + } + } + + // The current DeferredCloses object. + static DeferredCloses* current_; + + typedef std::queue< scoped_refptr<RenderWidget> > WidgetQueue; + WidgetQueue queue_; +}; + +DeferredCloses* DeferredCloses::current_ = NULL; + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// + +RenderWidget::RenderWidget() + : routing_id_(MSG_ROUTING_NONE), + opener_id_(MSG_ROUTING_NONE), + host_window_(NULL), + current_paint_buf_(NULL), + current_scroll_buf_(NULL), + next_paint_flags_(0), + paint_reply_pending_(false), + did_show_(false), + closing_(false), + is_hidden_(false), + needs_repainting_on_restore_(false), + has_focus_(false), + ime_is_active_(false), + ime_control_enable_ime_(true), + ime_control_x_(-1), + ime_control_y_(-1), + ime_control_new_state_(false), + ime_control_updated_(false) { + RenderProcess::AddRefProcess(); +} + +RenderWidget::~RenderWidget() { + if (current_paint_buf_) { + RenderProcess::FreeSharedMemory(current_paint_buf_); + current_paint_buf_ = NULL; + } + if (current_scroll_buf_) { + RenderProcess::FreeSharedMemory(current_scroll_buf_); + current_scroll_buf_ = NULL; + } + RenderProcess::ReleaseProcess(); +} + +/*static*/ +RenderWidget* RenderWidget::Create(int32 opener_id) { + DCHECK(opener_id != MSG_ROUTING_NONE); + scoped_refptr<RenderWidget> widget = new RenderWidget(); + widget->Init(opener_id); // adds reference + return widget; +} + +void RenderWidget::Init(int32 opener_id) { + DCHECK(!webwidget_); + + if (opener_id != MSG_ROUTING_NONE) + opener_id_ = opener_id; + + // Avoid a leak here by not assigning, since WebWidget::Create addrefs for us. + WebWidget* webwidget = WebWidget::Create(this); + webwidget_.swap(&webwidget); + + bool result = RenderThread::current()->Send( + new ViewHostMsg_CreateWidget(opener_id, &routing_id_)); + if (result) { + RenderThread::current()->AddRoute(routing_id_, this); + // Take a reference on behalf of the RenderThread. This will be balanced + // when we receive ViewMsg_Close. + AddRef(); + } else { + DCHECK(false); + } +} + +// This is used to complete pending inits and non-pending inits. For non- +// pending cases, the parent will be the same as the current parent. This +// indicates we do not need to reparent or anything. +void RenderWidget::CompleteInit(HWND parent_hwnd) { + DCHECK(routing_id_ != MSG_ROUTING_NONE); + DCHECK(parent_hwnd); + + host_window_ = parent_hwnd; + + Send(new ViewHostMsg_RendererReady(routing_id_)); +} + +IPC_DEFINE_MESSAGE_MAP(RenderWidget) + IPC_MESSAGE_HANDLER(ViewMsg_Close, OnClose) + IPC_MESSAGE_HANDLER(ViewMsg_CreatingNew_ACK, OnCreatingNewAck) + IPC_MESSAGE_HANDLER(ViewMsg_Resize, OnResize) + IPC_MESSAGE_HANDLER(ViewMsg_WasHidden, OnWasHidden) + IPC_MESSAGE_HANDLER(ViewMsg_WasRestored, OnWasRestored) + IPC_MESSAGE_HANDLER(ViewMsg_PaintRect_ACK, OnPaintRectAck) + IPC_MESSAGE_HANDLER(ViewMsg_ScrollRect_ACK, OnScrollRectAck) + IPC_MESSAGE_HANDLER(ViewMsg_HandleInputEvent, OnHandleInputEvent) + IPC_MESSAGE_HANDLER(ViewMsg_MouseCaptureLost, OnMouseCaptureLost) + IPC_MESSAGE_HANDLER(ViewMsg_SetFocus, OnSetFocus) + IPC_MESSAGE_HANDLER(ViewMsg_ImeSetInputMode, OnImeSetInputMode) + IPC_MESSAGE_HANDLER(ViewMsg_ImeSetComposition, OnImeSetComposition) + IPC_MESSAGE_UNHANDLED_ERROR() +IPC_END_MESSAGE_MAP() + +bool RenderWidget::Send(IPC::Message* message) { + // Don't send any messages after the browser has told us to close. + if (closing_) { + delete message; + return false; + } + + // If given a messsage without a routing ID, then assign our routing ID. + if (message->routing_id() == MSG_ROUTING_NONE) + message->set_routing_id(routing_id_); + + bool rv = RenderThread::current()->Send(message); + + // If there aren't any more RenderThread::Send calls on the stack, then we + // can go ahead and schedule Close to be called on any RenderWidget objects + // that received a ViewMsg_Close while we were inside Send. + if (!RenderThread::current()->in_send()) + DeferredCloses::Post(); + + return rv; +} + +// Got a response from the browser after the renderer decided to create a new +// view. +void RenderWidget::OnCreatingNewAck(HWND parent) { + DCHECK(routing_id_ != MSG_ROUTING_NONE); + + CompleteInit(parent); +} + +void RenderWidget::OnClose() { + if (closing_) + return; + closing_ = true; + + // Browser correspondence is no longer needed at this point. + if (routing_id_ != MSG_ROUTING_NONE) + RenderThread::current()->RemoveRoute(routing_id_); + + // Balances the AddRef taken when we called AddRoute. This release happens + // via the MessageLoop since it may cause our destruction. + MessageLoop::current()->ReleaseSoon(FROM_HERE, this); + + // If there is a Send call on the stack, then it could be dangerous to close + // now. Instead, we wait until we get out of Send. + if (RenderThread::current()->in_send()) { + DeferredCloses::Push(this); + } else { + Close(); + } +} + +void RenderWidget::OnResize(const gfx::Size& new_size) { + // During shutdown we can just ignore this message. + if (!webwidget_) + return; + + // TODO(darin): We should not need to reset this here. + is_hidden_ = false; + needs_repainting_on_restore_ = false; + + // We shouldn't be asked to resize to our current size. + DCHECK(size_ != new_size); + size_ = new_size; + + // We should not be sent a Resize message if we have not ACK'd the previous + DCHECK(!next_paint_is_resize_ack()); + + // When resizing, we want to wait to paint before ACK'ing the resize. This + // ensures that we only resize as fast as we can paint. We only need to send + // an ACK if we are resized to a non-empty rect. + webwidget_->Resize(new_size); + if (!new_size.IsEmpty()) { + DCHECK(!paint_rect_.IsEmpty()); + + // This should have caused an invalidation of the entire view. The damaged + // rect could be larger than new_size if we are being made smaller. + DCHECK_GE(paint_rect_.width(), new_size.width()); + DCHECK_GE(paint_rect_.height(), new_size.height()); + + // We will send the Resize_ACK flag once we paint again. + set_next_paint_is_resize_ack(); + } +} + +void RenderWidget::OnWasHidden() { + // Go into a mode where we stop generating paint and scrolling events. + is_hidden_ = true; +} + +void RenderWidget::OnWasRestored(bool needs_repainting) { + // During shutdown we can just ignore this message. + if (!webwidget_) + return; + + // See OnWasHidden + is_hidden_ = false; + + if (!needs_repainting && !needs_repainting_on_restore_) + return; + needs_repainting_on_restore_ = false; + + // Tag the next paint as a restore ack, which is picked up by DoDeferredPaint + // when it sends out the next PaintRect message. + set_next_paint_is_restore_ack(); + + // Generate a full repaint. + DidInvalidateRect(webwidget_, gfx::Rect(size_.width(), size_.height())); +} + +void RenderWidget::OnPaintRectAck() { + DCHECK(paint_reply_pending()); + paint_reply_pending_ = false; + + // If we sent a PaintRect message with a zero-sized bitmap, then + // we should have no current paint buf. + if (current_paint_buf_) { + RenderProcess::FreeSharedMemory(current_paint_buf_); + current_paint_buf_ = NULL; + } + + // Continue painting if necessary... + DoDeferredPaint(); +} + +void RenderWidget::OnScrollRectAck() { + DCHECK(scroll_reply_pending()); + + RenderProcess::FreeSharedMemory(current_scroll_buf_); + current_scroll_buf_ = NULL; + + // Continue scrolling if necessary... + DoDeferredScroll(); +} + +void RenderWidget::OnHandleInputEvent(const IPC::Message& message) { + void* iter = NULL; + + const char* data; + int data_length; + if (!message.ReadData(&iter, &data, &data_length)) + return; + + const WebInputEvent* input_event = + reinterpret_cast<const WebInputEvent*>(data); + bool processed = false; + if (webwidget_) + processed = webwidget_->HandleInputEvent(input_event); + + IPC::Message* response = new ViewHostMsg_HandleInputEvent_ACK(routing_id_); + response->WriteInt(input_event->type); + if (!processed) { + // If the event was not processed we send it back. + response->WriteData(data, data_length); + } + Send(response); +} + +void RenderWidget::OnMouseCaptureLost() { + if (webwidget_) + webwidget_->MouseCaptureLost(); +} + +void RenderWidget::OnSetFocus(bool enable) { + has_focus_ = enable; + if (webwidget_) + webwidget_->SetFocus(enable); + if (enable) { + // Force to retrieve the state of the focused widget to determine if we + // should activate IMEs next time when this process calls the UpdateIME() + // function. + ime_control_updated_ = true; + ime_control_new_state_ = true; + } +} + +void RenderWidget::ClearFocus() { + // We may have got the focus from the browser before this gets processed, in + // which case we do not want to unfocus ourself. + if (!has_focus_ && webwidget_) + webwidget_->SetFocus(false); +} + +void RenderWidget::PaintRect(const gfx::Rect& rect, SharedMemory* paint_buf) { + gfx::PlatformCanvas canvas(rect.width(), rect.height(), true, + paint_buf->handle()); + // Bring the canvas into the coordinate system of the paint rect + canvas.translate(static_cast<SkScalar>(-rect.x()), + static_cast<SkScalar>(-rect.y())); + + webwidget_->Paint(&canvas, rect); + + // Flush to underlying bitmap. TODO(darin): is this needed? + canvas.getTopPlatformDevice().accessBitmap(false); + + // Let the subclass observe this paint operations. + DidPaint(); +} + +size_t RenderWidget::GetPaintBufSize(const gfx::Rect& rect) { + // TODO(darin): protect against overflow + return 4 * rect.width() * rect.height(); +} + +void RenderWidget::DoDeferredPaint() { + if (!webwidget_ || paint_reply_pending() || paint_rect_.IsEmpty()) + return; + + // When we are hidden, we want to suppress painting, but we still need to + // mark this DoDeferredPaint as complete. + if (is_hidden_ || size_.IsEmpty()) { + paint_rect_ = gfx::Rect(); + needs_repainting_on_restore_ = true; + return; + } + + // Layout may generate more invalidation... + webwidget_->Layout(); + + // OK, save the current paint_rect to a local since painting may cause more + // invalidation. Some WebCore rendering objects only layout when painted. + gfx::Rect damaged_rect = paint_rect_; + paint_rect_ = gfx::Rect(); + + // Compute a buffer for painting and cache it. + current_paint_buf_ = + RenderProcess::AllocSharedMemory(GetPaintBufSize(damaged_rect)); + if (!current_paint_buf_) { + NOTREACHED(); + return; + } + + PaintRect(damaged_rect, current_paint_buf_); + + ViewHostMsg_PaintRect_Params params; + params.bitmap = current_paint_buf_->handle(); + params.bitmap_rect = damaged_rect; + params.view_size = size_; + params.plugin_window_moves = plugin_window_moves_; + params.flags = next_paint_flags_; + + plugin_window_moves_.clear(); + + paint_reply_pending_ = true; + Send(new ViewHostMsg_PaintRect(routing_id_, params)); + next_paint_flags_ = 0; + + UpdateIME(); +} + +void RenderWidget::DoDeferredScroll() { + if (!webwidget_ || scroll_reply_pending() || scroll_rect_.IsEmpty()) + return; + + // When we are hidden, we want to suppress scrolling, but we still need to + // mark this DoDeferredScroll as complete. + if (is_hidden_ || size_.IsEmpty()) { + scroll_rect_ = gfx::Rect(); + needs_repainting_on_restore_ = true; + return; + } + + // Layout may generate more invalidation, so we might have to bail on + // optimized scrolling... + webwidget_->Layout(); + + if (scroll_rect_.IsEmpty()) + return; + + gfx::Rect damaged_rect; + + // Compute the region we will expose by scrolling, and paint that into a + // shared memory section. + if (scroll_delta_.x()) { + int dx = scroll_delta_.x(); + damaged_rect.set_y(scroll_rect_.y()); + damaged_rect.set_height(scroll_rect_.height()); + if (dx > 0) { + damaged_rect.set_x(scroll_rect_.x()); + damaged_rect.set_width(dx); + } else { + damaged_rect.set_x(scroll_rect_.right() + dx); + damaged_rect.set_width(-dx); + } + } else { + int dy = scroll_delta_.y(); + damaged_rect.set_x(scroll_rect_.x()); + damaged_rect.set_width(scroll_rect_.width()); + if (dy > 0) { + damaged_rect.set_y(scroll_rect_.y()); + damaged_rect.set_height(dy); + } else { + damaged_rect.set_y(scroll_rect_.bottom() + dy); + damaged_rect.set_height(-dy); + } + } + + // In case the scroll offset exceeds the width/height of the scroll rect + damaged_rect = scroll_rect_.Intersect(damaged_rect); + + current_scroll_buf_ = + RenderProcess::AllocSharedMemory(GetPaintBufSize(damaged_rect)); + if (!current_scroll_buf_) { + NOTREACHED(); + return; + } + + // Set these parameters before calling Paint, since that could result in + // further invalidates (uncommon). + ViewHostMsg_ScrollRect_Params params; + params.bitmap = current_scroll_buf_->handle(); + params.bitmap_rect = damaged_rect; + params.dx = scroll_delta_.x(); + params.dy = scroll_delta_.y(); + params.clip_rect = scroll_rect_; + params.view_size = size_; + params.plugin_window_moves = plugin_window_moves_; + + plugin_window_moves_.clear(); + + // Mark the scroll operation as no longer pending. + scroll_rect_ = gfx::Rect(); + + PaintRect(damaged_rect, current_scroll_buf_); + Send(new ViewHostMsg_ScrollRect(routing_id_, params)); + UpdateIME(); +} + +/////////////////////////////////////////////////////////////////////////////// +// WebWidgetDelegate + +HWND RenderWidget::GetContainingWindow(WebWidget* webwidget) { + return host_window_; +} + +void RenderWidget::DidInvalidateRect(WebWidget* webwidget, + const gfx::Rect& rect) { + // We only want one pending DoDeferredPaint call at any time... + bool paint_pending = !paint_rect_.IsEmpty(); + + // If this invalidate overlaps with a pending scroll, then we have to + // downgrade to invalidating the scroll rect. + if (rect.Intersects(scroll_rect_)) { + paint_rect_ = paint_rect_.Union(scroll_rect_); + scroll_rect_ = gfx::Rect(); + } + + gfx::Rect view_rect(0, 0, size_.width(), size_.height()); + // TODO(iyengar) Investigate why we have painting issues when + // we ignore invalid regions outside the view. + // Ignore invalidates that occur outside the bounds of the view + // TODO(darin): maybe this should move into the paint code? + // paint_rect_ = view_rect.Intersect(paint_rect_.Union(rect)); + paint_rect_ = paint_rect_.Union(view_rect.Intersect(rect)); + + if (paint_rect_.IsEmpty() || paint_reply_pending() || paint_pending) + return; + + // Perform painting asynchronously. This serves two purposes: + // 1) Ensures that we call WebView::Paint without a bunch of other junk + // on the call stack. + // 2) Allows us to collect more damage rects before painting to help coalesce + // the work that we will need to do. + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + this, &RenderWidget::DoDeferredPaint)); +} + +void RenderWidget::DidScrollRect(WebWidget* webwidget, int dx, int dy, + const gfx::Rect& clip_rect) { + // We only support scrolling along one axis at a time. + DCHECK((dx && !dy) || (!dx && dy)); + + bool intersects_with_painting = paint_rect_.Intersects(clip_rect); + + // If we already have a pending scroll operation or if this scroll operation + // intersects the existing paint region, then just failover to invalidating. + if (!scroll_rect_.IsEmpty() || intersects_with_painting) { + if (!intersects_with_painting && scroll_rect_ == clip_rect) { + // OK, we can just update the scroll delta (requires same scrolling axis) + if (!dx && !scroll_delta_.x()) { + scroll_delta_.set_y(scroll_delta_.y() + dy); + return; + } + if (!dy && !scroll_delta_.y()) { + scroll_delta_.set_x(scroll_delta_.x() + dx); + return; + } + } + DidInvalidateRect(webwidget_, scroll_rect_); + DCHECK(scroll_rect_.IsEmpty()); + DidInvalidateRect(webwidget_, clip_rect); + return; + } + + // We only want one pending DoDeferredScroll call at any time... + bool scroll_pending = !scroll_rect_.IsEmpty(); + + scroll_rect_ = clip_rect; + scroll_delta_.SetPoint(dx, dy); + + if (scroll_pending) + return; + + // Perform scrolling asynchronously since we need to call WebView::Paint + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + this, &RenderWidget::DoDeferredScroll)); +} + +void RenderWidget::SetCursor(WebWidget* webwidget, const WebCursor& cursor) { + // Only send a SetCursor message if we need to make a change. + if (!current_cursor_.IsEqual(cursor)) { + current_cursor_ = cursor; + Send(new ViewHostMsg_SetCursor(routing_id_, cursor)); + } +} + +// We are supposed to get a single call to Show for a newly created RenderWidget +// that was created via RenderWidget::CreateWebView. So, we wait until this +// point to dispatch the ShowWidget message. +// +// This method provides us with the information about how to display the newly +// created RenderWidget (i.e., as a constrained popup or as a new tab). +// +void RenderWidget::Show(WebWidget* webwidget, + WindowOpenDisposition disposition) { + DCHECK(!did_show_) << "received extraneous Show call"; + DCHECK(routing_id_ != MSG_ROUTING_NONE); + DCHECK(opener_id_ != MSG_ROUTING_NONE); + + if (!did_show_) { + did_show_ = true; + // NOTE: initial_pos_ may still have its default values at this point, but + // that's okay. It'll be ignored if as_popup is false, or the browser + // process will impose a default position otherwise. + RenderThread::current()->Send(new ViewHostMsg_ShowWidget( + opener_id_, routing_id_, initial_pos_)); + } +} + +void RenderWidget::Focus(WebWidget* webwidget) { + // Prevent the widget from stealing the focus if it does not have focus + // already. We do this by explicitely setting the focus to false again. + // We only let the browser focus the renderer. + if (!has_focus_ && webwidget_) { + MessageLoop::current()->PostTask(FROM_HERE, + NewRunnableMethod(this, &RenderWidget::ClearFocus)); + } +} + +void RenderWidget::Blur(WebWidget* webwidget) { + Send(new ViewHostMsg_Blur(routing_id_)); +} + +void RenderWidget::CloseWidgetSoon(WebWidget* webwidget) { + // If a page calls window.close() twice, we'll end up here twice, but that's + // OK. It is safe to send multiple Close messages. + + // Ask the RenderWidgetHost to initiate close. + Send(new ViewHostMsg_Close(routing_id_)); +} + +void RenderWidget::Close() { + if (webwidget_) { + webwidget_->Close(); + webwidget_ = NULL; + } +} + +void RenderWidget::GetWindowLocation(WebWidget* webwidget, gfx::Point* origin) { + gfx::Rect rect; + Send(new ViewHostMsg_GetWindowRect(routing_id_, host_window_, &rect)); + *origin = rect.origin(); +} + +void RenderWidget::SetWindowRect(WebWidget* webwidget, const gfx::Rect& pos) { + if (did_show_) { + Send(new ViewHostMsg_RequestMove(routing_id_, pos)); + } else { + initial_pos_ = pos; + } +} + +void RenderWidget::OnImeSetInputMode(bool is_active) { + // A renderer process may move its input focus and the caret position + // while a browser process stop receiving IPC messages. + // Thus, when a browser process requests for a renderer process to send + // IPC messages, it has to check whether or not a renderer process moves + // its input focus and send an IPC message if they are updated. + ime_is_active_ = is_active; + ime_control_updated_ = true; + ime_control_new_state_ = true; +} + +void RenderWidget::OnImeSetComposition(int string_type, + int cursor_position, + int target_start, int target_end, + const std::wstring& ime_string) { + if (webwidget_) { + int string_length = static_cast<int>(ime_string.length()); + const wchar_t* string_data = ime_string.data(); + webwidget_->ImeSetComposition(string_type, cursor_position, + target_start, target_end, + string_length, string_data); + } +} + +void RenderWidget::UpdateIME() { + // If a browser process does not have IMEs, its IMEs are not active, or there + // are not any attached widgets. + // a renderer process does not have to retrieve information of the focused + // control or send notification messages to a browser process. + if (!ime_is_active_) { + return; + } + // Retrieve the caret position from the focused widget. + bool enable_ime; + int x, y; + const void *id; + if (!webwidget_ || !webwidget_->ImeUpdateStatus(&enable_ime, &id, &x, &y)) { + // There are not any editable widgets attached to this process. + // We should disable the IME to prevent it from sending CJK strings to + // non-editable widgets. + ime_control_updated_ = true; + ime_control_new_state_ = false; + } + if (ime_control_updated_) { + // The input focus has been changed. + // Compare the current state with the updated state and choose actions. + if (ime_control_enable_ime_) { + if (ime_control_new_state_) { + // Case 1: a text input -> another text input + // Complete the current composition and notify the caret position. + enum ViewHostMsg_ImeControl control = IME_COMPLETE_COMPOSITION; + Send(new ViewHostMsg_ImeUpdateStatus(routing_id(), control, x, y)); + } else { + // Case 2: a text input -> a password input (or a static control) + // Complete the current composition and disable the IME. + enum ViewHostMsg_ImeControl control = IME_DISABLE; + Send(new ViewHostMsg_ImeUpdateStatus(routing_id(), control, x, y)); + } + } else { + if (ime_control_new_state_) { + // Case 3: a password input (or a static control) -> a text input + // Enable the IME and notify the caret position. + enum ViewHostMsg_ImeControl control = IME_COMPLETE_COMPOSITION; + Send(new ViewHostMsg_ImeUpdateStatus(routing_id(), control, x, y)); + } else { + // Case 4: a password input (or a static contol) -> another password + // input (or another static control). + // The IME has been already disabled and we don't have to do anything. + } + } + } else { + // The input focus is not changed. + // Notify the caret position to a browser process only if it is changed. + if (ime_control_enable_ime_) { + if (x != ime_control_x_ || y != ime_control_y_) { + enum ViewHostMsg_ImeControl control = IME_MOVE_WINDOWS; + Send(new ViewHostMsg_ImeUpdateStatus(routing_id(), control, x, y)); + } + } + } + // Save the updated IME status to prevent from sending the same IPC messages. + ime_control_updated_ = false; + ime_control_enable_ime_ = ime_control_new_state_; + ime_control_x_ = x; + ime_control_y_ = y; +} + +void RenderWidget::DidMove(WebWidget* webwidget, + const WebPluginGeometry& move) { + size_t i = 0; + for (; i < plugin_window_moves_.size(); ++i) { + if (plugin_window_moves_[i].window == move.window) { + plugin_window_moves_[i] = move; + break; + } + } + + if (i == plugin_window_moves_.size()) + plugin_window_moves_.push_back(move); +} diff --git a/chrome/renderer/render_widget.h b/chrome/renderer/render_widget.h new file mode 100644 index 0000000..720df07 --- /dev/null +++ b/chrome/renderer/render_widget.h @@ -0,0 +1,277 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_RENDERER_RENDER_WIDGET_H__ +#define CHROME_RENDERER_RENDER_WIDGET_H__ + +#include <vector> +#include "base/basictypes.h" +#include "base/gfx/point.h" +#include "base/gfx/rect.h" +#include "base/gfx/size.h" +#include "base/ref_counted.h" +#include "chrome/common/ipc_channel.h" +#include "chrome/common/render_messages.h" +#include "chrome/renderer/render_process.h" +#include "webkit/glue/webwidget_delegate.h" +#include "webkit/glue/webcursor.h" +#include "webkit/glue/webplugin.h" + +// RenderWidget provides a communication bridge between a WebWidget and +// a RenderWidgetHost, the latter of which lives in a different process. +class RenderWidget : public IPC::Channel::Listener, + public IPC::Message::Sender, + virtual public WebWidgetDelegate, + public base::RefCounted<RenderWidget> { + public: + // Creates a new RenderWidget. The opener_id is the routing ID of the + // RenderView that this widget lives inside. + static RenderWidget* Create(int32 opener_id); + + // The routing ID assigned by the RenderProcess. Will be MSG_ROUTING_NONE if + // not yet assigned a view ID, in which case, the process MUST NOT send + // messages with this ID to the parent. + int32 routing_id() const { + return routing_id_; + } + + // May return NULL when the window is closing. + WebWidget* webwidget() const { + return webwidget_; + } + + // Implementing RefCounting required for WebWidgetDelegate + virtual void AddRef() { + RefCounted<RenderWidget>::AddRef(); + } + virtual void Release() { + RefCounted<RenderWidget>::Release(); + } + + // IPC::Channel::Listener + virtual void OnMessageReceived(const IPC::Message& msg); + + // IPC::Message::Sender + virtual bool Send(IPC::Message* msg); + + // WebWidgetDelegate + virtual HWND GetContainingWindow(WebWidget* webwidget); + virtual void DidInvalidateRect(WebWidget* webwidget, const gfx::Rect& rect); + virtual void DidScrollRect(WebWidget* webwidget, int dx, int dy, + const gfx::Rect& clip_rect); + virtual void SetCursor(WebWidget* webwidget, const WebCursor& cursor); + virtual void Show(WebWidget* webwidget, WindowOpenDisposition disposition); + virtual void CloseWidgetSoon(WebWidget* webwidget); + virtual void Focus(WebWidget* webwidget); + virtual void Blur(WebWidget* webwidget); + virtual void GetWindowLocation(WebWidget* webwidget, gfx::Point* origin); + virtual void SetWindowRect(WebWidget* webwidget, const gfx::Rect& rect); + virtual void DidMove(WebWidget* webwidget, const WebPluginGeometry& move); + virtual void RunModal(WebWidget* webwidget) {} + + // Do not delete directly. This class is reference counted. + virtual ~RenderWidget(); + + // Close the underlying WebWidget. + void Close(); + + protected: + RenderWidget(); + + // Initializes this view with the given opener. CompleteInit must be called + // later. + void Init(int32 opener_id); + + // Finishes creation of a pending view started with Init. + void CompleteInit(HWND parent); + + // Paints the given rectangular region of the WebWidget into paint_buf (a + // shared memory segment returned by AllocPaintBuf). The caller must ensure + // that the given rect fits within the bounds of the WebWidget. + void PaintRect(const gfx::Rect& rect, SharedMemory* paint_buf); + + // Get the size of the paint buffer for the given rectangle, rounding up to + // the allocation granularity of the system. + size_t GetPaintBufSize(const gfx::Rect& rect); + + void DoDeferredPaint(); + void DoDeferredScroll(); + + // This method is called immediately after PaintRect but before the + // corresponding paint or scroll message is send to the widget host. + virtual void DidPaint() {} + + // RenderWidget IPC message handlers + void OnClose(); + void OnCreatingNewAck(HWND parent); + void OnResize(const gfx::Size& new_size); + void OnWasHidden(); + void OnWasRestored(bool needs_repainting); + void OnPaintRectAck(); + void OnScrollRectAck(); + void OnHandleInputEvent(const IPC::Message& message); + void OnMouseCaptureLost(); + void OnSetFocus(bool enable); + void OnImeSetInputMode(bool is_active); + void OnImeSetComposition(int string_type, int cursor_position, + int target_start, int target_end, + const std::wstring& ime_string); + + // True if a PaintRect_ACK message is pending. + bool paint_reply_pending() const { + return paint_reply_pending_; + } + + // True if a ScrollRect_ACK message is pending. + bool scroll_reply_pending() const { + return current_scroll_buf_ != NULL; + } + + bool next_paint_is_resize_ack() const { + return ViewHostMsg_PaintRect_Flags::is_resize_ack(next_paint_flags_); + } + + bool next_paint_is_restore_ack() const { + return ViewHostMsg_PaintRect_Flags::is_restore_ack(next_paint_flags_); + } + + void set_next_paint_is_resize_ack() { + next_paint_flags_ |= ViewHostMsg_PaintRect_Flags::IS_RESIZE_ACK; + } + + void set_next_paint_is_restore_ack() { + next_paint_flags_ |= ViewHostMsg_PaintRect_Flags::IS_RESTORE_ACK; + } + + // Called when a renderer process moves an input focus or updates the + // position of its caret. + // This function compares them with the previous values, and send them to + // the browser process only if they are updated. + // The browser process moves IME windows and context. + void UpdateIME(); + + // Tells the renderer it does not have focus. Used to prevent us from getting + // the focus on our own when the browser did not focus us. + void ClearFocus(); + + // Routing ID that allows us to communicate to the parent browser process + // RenderWidgetHost. When MSG_ROUTING_NONE, no messages may be sent. + int32 routing_id_; + + scoped_refptr<WebWidget> webwidget_; + + // Set to the ID of the view that initiated creating this view, if any. When + // the view was initiated by the browser (the common case), this will be + // MSG_ROUTING_NONE. This is used in determining ownership when opening + // child tabs. See RenderWidget::createWebViewWithRequest. + // + // This ID may refer to an invalid view if that view is closed before this + // view is. + int32 opener_id_; + + // The position where this view should be initially shown. + gfx::Rect initial_pos_; + + // The window we are embedded within. TODO(darin): kill this. + HWND host_window_; + + // We store the current cursor object so we can avoid spamming SetCursor + // messages. + WebCursor current_cursor_; + // The size of the RenderWidget. + gfx::Size size_; + + // Shared memory handles that are currently in use to transfer an image to + // the browser. + SharedMemory* current_paint_buf_; + SharedMemory* current_scroll_buf_; + + // The smallest bounding rectangle that needs to be re-painted. This is non- + // empty if a paint event is pending. + gfx::Rect paint_rect_; + + // The clip rect for the pending scroll event. This is non-empty if a + // scroll event is pending. + gfx::Rect scroll_rect_; + + // The scroll delta for a pending scroll event. + gfx::Point scroll_delta_; + + // Flags for the next ViewHostMsg_PaintRect message. + int next_paint_flags_; + + // True if we are expecting a PaintRect_ACK message (i.e., that a PaintRect + // message has been sent). + bool paint_reply_pending_; + + // Set to true if we should ignore RenderWidget::Show calls. + bool did_show_; + + // Indicates that we shouldn't bother generated paint events. + bool is_hidden_; + + // Indicates that we should be repainted when restored. This flag is set to + // true if we receive an invalidation / scroll event from webkit while our + // is_hidden_ flag is set to true. This is used to force a repaint once we + // restore to account for the fact that our host would not know about the + // invalidation / scroll event(s) from webkit while we are hidden. + bool needs_repainting_on_restore_; + + // Indicates whether we have been focused/unfocused by the browser. + bool has_focus_; + + // True if we have requested this widget be closed. No more messages will + // be sent, except for a Close. + bool closing_; + + // Represents whether or not the IME of a browser process is active. + bool ime_is_active_; + + // Represents the status of the selected edit control sent to a browser + // process last time. + // When a renderer process finishes rendering a region, it retrieves: + // * The identifier of the selected edit control; + // * Whether or not the selected edit control requires IME, and; + // * The position of the caret (or cursor). + // If the above values is updated, a renderer process sends an IPC message + // to a browser process. A browser process uses these values to + // activate/deactivate IME and set the position of IME windows. + bool ime_control_enable_ime_; + int ime_control_x_; + int ime_control_y_; + bool ime_control_new_state_; + bool ime_control_updated_; + + // Holds all the needed plugin window moves for a scroll. + std::vector<WebPluginGeometry> plugin_window_moves_; + + DISALLOW_EVIL_CONSTRUCTORS(RenderWidget); +}; + +#endif // CHROME_RENDERER_RENDER_WIDGET_H__ diff --git a/chrome/renderer/renderer.vcproj b/chrome/renderer/renderer.vcproj new file mode 100644 index 0000000..7f867bd --- /dev/null +++ b/chrome/renderer/renderer.vcproj @@ -0,0 +1,283 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="8.00" + Name="renderer" + ProjectGUID="{9301A569-5D2B-4D11-9332-B1E30AEACB8D}" + RootNamespace="renderer" + Keyword="Win32Proj" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + ConfigurationType="4" + InheritedPropertySheets=".\renderer.vsprops;$(SolutionDir)..\build\debug.vsprops;..\tools\build\win\precompiled_wtl.vsprops" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + ConfigurationType="4" + InheritedPropertySheets=".\renderer.vsprops;$(SolutionDir)..\build\release.vsprops" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="automation" + > + <File + RelativePath=".\automation\dom_automation_controller.cc" + > + </File> + <File + RelativePath=".\automation\dom_automation_controller.h" + > + </File> + </Filter> + <Filter + Name="net" + > + <File + RelativePath=".\net\render_dns_master.cc" + > + </File> + <File + RelativePath=".\net\render_dns_master.h" + > + </File> + <File + RelativePath=".\net\render_dns_queue.cc" + > + </File> + <File + RelativePath=".\net\render_dns_queue.h" + > + </File> + </Filter> + <File + RelativePath=".\about_handler.cc" + > + </File> + <File + RelativePath=".\about_handler.h" + > + </File> + <File + RelativePath=".\debug_message_handler.cc" + > + </File> + <File + RelativePath=".\debug_message_handler.h" + > + </File> + <File + RelativePath=".\dom_ui_bindings.cc" + > + </File> + <File + RelativePath=".\dom_ui_bindings.h" + > + </File> + <File + RelativePath=".\external_js_object.cc" + > + </File> + <File + RelativePath=".\external_js_object.h" + > + </File> + <File + RelativePath=".\localized_error.cc" + > + </File> + <File + RelativePath=".\localized_error.h" + > + </File> + <File + RelativePath=".\plugin_channel_host.cc" + > + </File> + <File + RelativePath=".\plugin_channel_host.h" + > + </File> + <File + RelativePath="..\tools\build\win\precompiled_wtl.cc" + > + <FileConfiguration + Name="Debug|Win32" + > + <Tool + Name="VCCLCompilerTool" + UsePrecompiledHeader="1" + /> + </FileConfiguration> + </File> + <File + RelativePath="..\tools\build\win\precompiled_wtl.h" + > + </File> + <File + RelativePath=".\render_process.cc" + > + </File> + <File + RelativePath=".\render_process.h" + > + </File> + <File + RelativePath=".\render_thread.cc" + > + </File> + <File + RelativePath=".\render_thread.h" + > + </File> + <File + RelativePath=".\render_view.cc" + > + </File> + <File + RelativePath=".\render_view.h" + > + </File> + <File + RelativePath=".\render_widget.cc" + > + </File> + <File + RelativePath=".\render_widget.h" + > + </File> + <File + RelativePath=".\renderer_glue.cc" + > + </File> + <File + RelativePath=".\renderer_main.cc" + > + </File> + <File + RelativePath=".\renderer_resources.h" + > + </File> + <File + RelativePath=".\visitedlink_slave.cc" + > + </File> + <File + RelativePath=".\visitedlink_slave.h" + > + </File> + <File + RelativePath=".\webplugin_delegate_proxy.cc" + > + </File> + <File + RelativePath=".\webplugin_delegate_proxy.h" + > + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/chrome/renderer/renderer.vsprops b/chrome/renderer/renderer.vsprops new file mode 100644 index 0000000..19eda36 --- /dev/null +++ b/chrome/renderer/renderer.vsprops @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioPropertySheet + ProjectType="Visual C++" + Version="8.00" + Name="renderer" + InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\skia\using_skia.vsprops;$(SolutionDir)..\third_party\npapi\using_npapi.vsprops;..\tools\build\win\using_generated_strings.vsprops;..\..\third_party\icu38\build\using_icu.vsprops;$(SolutionDir)third_party\wtl\using_wtl.vsprops" + > + <Tool + Name="VCResourceCompilerTool" + AdditionalIncludeDirectories="$(IntDir)" + /> +</VisualStudioPropertySheet> diff --git a/chrome/renderer/renderer_glue.cc b/chrome/renderer/renderer_glue.cc new file mode 100644 index 0000000..cfe1d9c --- /dev/null +++ b/chrome/renderer/renderer_glue.cc @@ -0,0 +1,407 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file provides the embedder's side of random webkit glue functions. + +#include <windows.h> +#include <wininet.h> + +#include "base/command_line.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/string_util.h" +#include "chrome/renderer/net/render_dns_master.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/render_messages.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/common/resource_dispatcher.h" +#include "chrome/plugin/npobject_util.h" +#include "chrome/renderer/render_process.h" +#include "chrome/renderer/render_thread.h" +#include "chrome/renderer/render_view.h" +#include "chrome/renderer/visitedlink_slave.h" +#include "googleurl/src/gurl.h" +#include "googleurl/src/url_util.h" +#include "net/base/mime_util.h" +#include "webkit/glue/resource_type.h" +#include "webkit/glue/webframe.h" +#include "webkit/glue/webview.h" +#include "webkit/glue/webkit_glue.h" + +#include "SkBitmap.h" + +#include <strsafe.h> // note: per msdn docs, this must *follow* other includes + +template <typename T, size_t stack_capacity> +class ResizableStackArray { + public: + ResizableStackArray() + : cur_buffer_(stack_buffer_), cur_capacity_(stack_capacity) { + } + ~ResizableStackArray() { + FreeHeap(); + } + + T* get() const { + return cur_buffer_; + } + + T& operator[](size_t i) { + return cur_buffer_[i]; + } + + size_t capacity() const { + return cur_capacity_; + } + + void Resize(size_t new_size) { + if (new_size < cur_capacity_) + return; // already big enough + FreeHeap(); + cur_capacity_ = new_size; + cur_buffer_ = new T[new_size]; + } + + private: + // Resets the heap buffer, if any + void FreeHeap() { + if (cur_buffer_ != stack_buffer_) { + delete[] cur_buffer_; + cur_buffer_ = stack_buffer_; + cur_capacity_ = stack_capacity; + } + } + + T stack_buffer_[stack_capacity]; + T* cur_buffer_; + size_t cur_capacity_; +}; + +namespace webkit_glue { + +bool HistoryContains(const wchar_t* url, int url_length, + const char* document_host, int document_host_length, + bool is_dns_prefetch_enabled) { + if (url_length == 0) + return false; // Empty URLs are not visited. + + // Use big stack buffer to avoid allocation when possible. + url_parse::Parsed parsed; + url_canon::RawCanonOutput<2048> canon; + + if (!url_util::Canonicalize(url, url_length, NULL, &canon, &parsed)) + return false; // Invalid URLs are not visited. + + char* parsed_host = canon.data() + parsed.host.begin; + + // If the hostnames match or is_dns_prefetch_enabled is true, do the prefetch. + if (parsed.host.is_nonempty()) { + if (is_dns_prefetch_enabled || + (document_host_length > 0 && parsed.host.len == document_host_length && + strncmp(parsed_host, document_host, parsed.host.len) == 0)) + DnsPrefetchCString(parsed_host, parsed.host.len); + } + + return RenderThread::current()->visited_link_slave()-> + IsVisited(canon.data(), canon.length()); +} + +void DnsPrefetchUrl(const wchar_t* url, int url_length) { + if (url_length == 0) + return; // Empty URLs have no hostnames. + + // Use big stack buffer to avoid allocation when possible. + url_parse::Parsed parsed; + url_canon::RawCanonOutput<2048> canon; + + if (!url_util::Canonicalize(url, url_length, NULL, &canon, &parsed)) + return; // Invalid URLs don't have hostnames. + + // Call for prefetching without creating a std::string(). + if (parsed.host.is_nonempty()) + DnsPrefetchCString(canon.data() + parsed.host.begin, parsed.host.len); +} + +void PrecacheUrl(const wchar_t* url, int url_length) { + // TBD: jar: Need implementation that loads the targetted URL into our cache. + // For now, at least prefetch DNS lookup + DnsPrefetchUrl(url, url_length); +} + +void webkit_glue::AppendToLog(const char* file, int line, const char* msg) { + logging::LogMessage(file, line).stream() << msg; +} + +bool webkit_glue::GetMimeTypeFromExtension(std::wstring &ext, + std::string *mime_type) { + if (IsPluginProcess()) + return mime_util::GetMimeTypeFromExtension(ext, mime_type); + + // The sandbox restricts our access to the registry, so we need to proxy + // these calls over to the browser process. + DCHECK(mime_type->empty()); + RenderThread::current()->Send( + new ViewHostMsg_GetMimeTypeFromExtension(ext, mime_type)); + return !mime_type->empty(); +} + +bool webkit_glue::GetMimeTypeFromFile(const std::wstring &file_path, + std::string *mime_type) { + if (IsPluginProcess()) + return mime_util::GetMimeTypeFromFile(file_path, mime_type); + + // The sandbox restricts our access to the registry, so we need to proxy + // these calls over to the browser process. + DCHECK(mime_type->empty()); + RenderThread::current()->Send( + new ViewHostMsg_GetMimeTypeFromFile(file_path, mime_type)); + return !mime_type->empty(); +} + +bool webkit_glue::GetPreferredExtensionForMimeType(const std::string& mime_type, + std::wstring* ext) { + if (IsPluginProcess()) + return mime_util::GetPreferredExtensionForMimeType(mime_type, ext); + + // The sandbox restricts our access to the registry, so we need to proxy + // these calls over to the browser process. + DCHECK(ext->empty()); + RenderThread::current()->Send( + new ViewHostMsg_GetPreferredExtensionForMimeType(mime_type, ext)); + return !ext->empty(); +} + +IMLangFontLink2* webkit_glue::GetLangFontLink() { + return RenderProcess::GetLangFontLink(); +} + +std::wstring webkit_glue::GetLocalizedString(int message_id) { + return ResourceBundle::GetSharedInstance().GetLocalizedString(message_id); +} + +std::string webkit_glue::GetDataResource(int resource_id) { + return ResourceBundle::GetSharedInstance().GetDataResource(resource_id); +} + +HCURSOR webkit_glue::LoadCursor(int cursor_id) { + return ResourceBundle::GetSharedInstance().LoadCursor(cursor_id); +} + +// Clipboard glue + +void webkit_glue::ClipboardClear() { + RenderThread::current()->Send(new ViewHostMsg_ClipboardClear()); +} + +void webkit_glue::ClipboardWriteText(const std::wstring& text) { + RenderThread::current()->Send(new ViewHostMsg_ClipboardWriteText(text)); +} + +void webkit_glue::ClipboardWriteHTML(const std::wstring& html, + const GURL& url) { + RenderThread::current()->Send(new ViewHostMsg_ClipboardWriteHTML(html, url)); +} + +void webkit_glue::ClipboardWriteBookmark(const std::wstring& title, + const GURL& url) { + RenderThread::current()->Send( + new ViewHostMsg_ClipboardWriteBookmark(title, url)); +} + +// Here we need to do some work to marshal the bitmap through shared memory +void webkit_glue::ClipboardWriteBitmap(const SkBitmap& bitmap) { + size_t buf_size = bitmap.getSize(); + gfx::Size size(bitmap.width(), bitmap.height()); + + // Allocate a shared memory buffer to hold the bitmap bits + SharedMemory* shared_buf = + RenderProcess::AllocSharedMemory(buf_size); + if (!shared_buf) { + NOTREACHED(); + return; + } + if (!shared_buf->Map(buf_size)) { + NOTREACHED(); + return; + } + + // Copy the bits into shared memory + SkAutoLockPixels bitmap_lock(bitmap); + memcpy(shared_buf->memory(), bitmap.getPixels(), buf_size); + shared_buf->Unmap(); + + // Send the handle over synchronous IPC + RenderThread::current()->Send( + new ViewHostMsg_ClipboardWriteBitmap(shared_buf->handle(), size)); + + // The browser should be done with the bitmap now. It's our job to free + // the shared memory. + RenderProcess::FreeSharedMemory(shared_buf); +} + +void webkit_glue::ClipboardWriteWebSmartPaste() { + RenderThread::current()->Send(new ViewHostMsg_ClipboardWriteWebSmartPaste()); +} + +bool webkit_glue::ClipboardIsFormatAvailable(unsigned int format) { + bool result; + RenderThread::current()->Send( + new ViewHostMsg_ClipboardIsFormatAvailable(format, &result)); + return result; +} + +void webkit_glue::ClipboardReadText(std::wstring* result) { + RenderThread::current()->Send(new ViewHostMsg_ClipboardReadText(result)); +} + +void webkit_glue::ClipboardReadAsciiText(std::string* result) { + RenderThread::current()->Send(new ViewHostMsg_ClipboardReadAsciiText(result)); +} + +void webkit_glue::ClipboardReadHTML(std::wstring* markup, GURL* url) { + RenderThread::current()->Send(new ViewHostMsg_ClipboardReadHTML(markup, url)); +} + + +bool webkit_glue::GetApplicationDirectory(std::wstring *path) { + return PathService::Get(chrome::DIR_APP, path); +} + +GURL webkit_glue::GetInspectorURL() { + return GURL("chrome-resource://inspector/inspector.html"); +} + +std::string webkit_glue::GetUIResourceProtocol() { + return "chrome-resource"; +} + +bool webkit_glue::GetExeDirectory(std::wstring *path) { + return PathService::Get(base::DIR_EXE, path); +} + +bool webkit_glue::GetPlugins(bool refresh, + std::vector<WebPluginInfo>* plugins) { + return RenderThread::current()->Send( + new ViewHostMsg_GetPlugins(refresh, plugins)); +} + +bool webkit_glue::IsPluginRunningInRendererProcess() { + return !IsPluginProcess(); +} + +bool webkit_glue::EnsureFontLoaded(HFONT font) { + LOGFONT logfont; + GetObject(font, sizeof(LOGFONT), &logfont); + return RenderThread::current()->Send(new ViewHostMsg_LoadFont(logfont)); +} + +MONITORINFOEX webkit_glue::GetMonitorInfoForWindow(HWND window) { + MONITORINFOEX monitor_info; + RenderThread::current()->Send( + new ViewHostMsg_GetMonitorInfoForWindow(window, &monitor_info)); + return monitor_info; +} + +std::wstring webkit_glue::GetWebKitLocale() { + // The browser process should have passed the locale to the renderer via the + // --lang command line flag. + CommandLine parsed_command_line; + const std::wstring& lang = + parsed_command_line.GetSwitchValue(switches::kLang); + DCHECK(!lang.empty()); + return lang; +} + +#ifndef USING_SIMPLE_RESOURCE_LOADER_BRIDGE + +// Each RenderView has a ResourceDispatcher. In unit tests, this function may +// not work properly since there may be a ResourceDispatcher w/o a RenderView. +// The WebView's delegate may be null, which typically happens as a WebView is +// being closed (but it is also possible that it could be null at other times +// since WebView has a SetDelegate method). +static ResourceDispatcher* GetResourceDispatcher(WebFrame* frame) { + WebViewDelegate* d = frame->GetView()->GetDelegate(); + return d ? static_cast<RenderView*>(d)->resource_dispatcher() : NULL; +} + +// static factory function +ResourceLoaderBridge* ResourceLoaderBridge::Create( + WebFrame* webframe, + const std::string& method, + const GURL& url, + const GURL& policy_url, + const GURL& referrer, + const std::string& headers, + int load_flags, + int origin_pid, + ResourceType::Type resource_type, + bool mixed_content) { + // TODO(darin): we need to eliminate the webframe parameter because webkit + // does not always supply it (see ResourceHandle::loadResourceSynchronously). + // Instead we should add context to ResourceRequest, which will be easy to do + // once we merge to the latest WebKit (r23806 at least). + if (!webframe) { + NOTREACHED() << "no webframe"; + return NULL; + } + ResourceDispatcher* dispatcher = GetResourceDispatcher(webframe); + if (!dispatcher) { + DLOG(WARNING) << "no resource dispatcher"; + return NULL; + } + return dispatcher->CreateBridge(method, url, policy_url, referrer, headers, + load_flags, origin_pid, resource_type, + mixed_content, 0); +} + +void SetCookie(const GURL& url, const GURL& policy_url, + const std::string& cookie) { + RenderThread::current()->Send(new ViewHostMsg_SetCookie(url, policy_url, + cookie)); +} + +std::string GetCookies(const GURL& url, const GURL& policy_url) { + std::string cookies; + RenderThread::current()->Send(new ViewHostMsg_GetCookies(url, policy_url, + &cookies)); + return cookies; +} + +void NotifyCacheStats() { + // Update the browser about our cache + // NOTE: Since this can be called from the plugin process, we might not have + // a RenderThread. Do nothing in that case. + if (RenderThread::current()) + RenderThread::current()->InformHostOfCacheStatsLater(); +} + +#endif // !USING_SIMPLE_RESOURCE_LOADER_BRIDGE + +} // namespace webkit_glue diff --git a/chrome/renderer/renderer_main.cc b/chrome/renderer/renderer_main.cc new file mode 100644 index 0000000..b47b1ff --- /dev/null +++ b/chrome/renderer/renderer_main.cc @@ -0,0 +1,136 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "base/command_line.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_counters.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/env_util.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/logging_chrome.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/renderer/render_process.h" +#include "chrome/test/injection_test_dll.h" +#include "sandbox/src/sandbox.h" + +#include "generated_resources.h" + +// This function provides some ways to test crash and assertion handling +// behavior of the renderer. +static void HandleRendererErrorTestParameters(const CommandLine& command_line) { + // This parameter causes an assertion. + if (command_line.HasSwitch(switches::kRendererAssertTest)) { + DCHECK(false); + } + + // This parameter causes a null pointer crash (crash reporter trigger). + if (command_line.HasSwitch(switches::kRendererCrashTest)) { + int* bad_pointer = NULL; + *bad_pointer = 0; + } + + if (command_line.HasSwitch(switches::kRendererStartupDialog)) { + std::wstring title = l10n_util::GetString(IDS_PRODUCT_NAME); + title += L" renderer"; // makes attaching to process easier + MessageBox(NULL, L"renderer starting...", title.c_str(), + MB_OK | MB_SETFOREGROUND); + } +} + +// mainline routine for running as the Rendererer process +int RendererMain(CommandLine &parsed_command_line, int show_command, + sandbox::TargetServices* target_services) +{ + StatsScope<StatsCounterTimer> + startup_timer(chrome::Counters::renderer_main()); + + Thread::SetThreadName("Chrome_RendererMain", GetCurrentThreadId()); + + CoInitialize(NULL); + + DLOG(INFO) << "Started renderer with " << + parsed_command_line.command_line_string(); + + HMODULE sandbox_test_module = NULL; + bool no_sandbox = parsed_command_line.HasSwitch(switches::kNoSandbox); + if (target_services && !no_sandbox) { + // The command line might specify a test dll to load. + if (parsed_command_line.HasSwitch(switches::kTestSandbox)) { + std::wstring test_dll_name = + parsed_command_line.GetSwitchValue(switches::kTestSandbox); + sandbox_test_module = LoadLibrary(test_dll_name.c_str()); + DCHECK(sandbox_test_module); + } + } + + HandleRendererErrorTestParameters(parsed_command_line); + + std::wstring channel_name = + parsed_command_line.GetSwitchValue(switches::kProcessChannelID); + if (RenderProcess::GlobalInit(channel_name)) { + bool run_loop = true; + if (!no_sandbox) { + if (target_services) { + target_services->LowerToken(); + } else { + run_loop = false; + } + } + + if (sandbox_test_module) { + RunRendererTests run_security_tests = + reinterpret_cast<RunRendererTests>(GetProcAddress(sandbox_test_module, + kRenderTestCall)); + DCHECK(run_security_tests); + if (run_security_tests) { + int test_count = 0; + DLOG(INFO) << "Running renderer security tests"; + BOOL result = run_security_tests(&test_count); + DCHECK(result) << "Test number " << test_count << " has failed."; + // If we are in release mode, debug or crash the process. + if (!result) + __debugbreak(); + } + } + + startup_timer.Stop(); // End of Startup Time Measurement. + + if (run_loop) { + // Load the accelerator table from the browser executable and tell the + // message loop to use it when translating messages. + MessageLoop::current()->Run(); + } + } + RenderProcess::GlobalCleanup(); + + CoUninitialize(); + return 0; +} diff --git a/chrome/renderer/renderer_resources.h b/chrome/renderer/renderer_resources.h new file mode 100644 index 0000000..21cc7be --- /dev/null +++ b/chrome/renderer/renderer_resources.h @@ -0,0 +1,5 @@ +// TODO(tc): Come up with a way to automate the generation of these +// IDs so they don't collide with other rc files. +#define IDR_NET_ERROR_HTML 500 +#define IDR_INSECURE_CONTENT_STAMP 501 +#define IDR_ERROR_NO_DETAILS_HTML 502 diff --git a/chrome/renderer/renderer_resources.rc b/chrome/renderer/renderer_resources.rc new file mode 100644 index 0000000..55bcd0b --- /dev/null +++ b/chrome/renderer/renderer_resources.rc @@ -0,0 +1,18 @@ +// Resources used by renderer/*. +// +// Paths in this file are relative to SolutionDir. + +#ifdef APSTUDIO_INVOKED + #error // Don't open in the Visual Studio resource editor! +#endif //APSTUDIO_INVOKED + +#include "renderer\\renderer_resources.h" + +///////////////////////////////////////////////////////////////////////////// +// +// data resources +// + +IDR_NET_ERROR_HTML BINDATA "renderer\\resources\\neterror.html" +IDR_INSECURE_CONTENT_STAMP BINDATA "renderer\\resources\\insecure_content_stamp.png" +IDR_ERROR_NO_DETAILS_HTML BINDATA "renderer\\resources\\error_no_details.html"
\ No newline at end of file diff --git a/chrome/renderer/resources/error_no_details.html b/chrome/renderer/resources/error_no_details.html new file mode 100644 index 0000000..7264142a --- /dev/null +++ b/chrome/renderer/resources/error_no_details.html @@ -0,0 +1,74 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html id="t" jsvalues="dir:textdirection"> +<head> +<title jscontent="title"> +</title> +<style type="text/css"><!-- +body { + background-color: #fff; + color: #000; + font-family: arial,sans-serif; + font-size: 83%; + line-height: 120%; + max-width: 35em; + padding: 0.5em 1em; +} +li { + padding-bottom: .3em; +} +ul { + margin: .5em 0 0; + padding-bottom: 0; +} +h1 { + font-size: 1.5em; + margin-bottom: 1.5em; +} +h2 { + font-size: 1em; + font-weight: bold; + margin: 0 0 .5em; + padding: 0; +} +a { + color: #00c; +} +a:active { + color: #f00; +} +a:visited { + color: #551a8b; +} +#errorSummary, #suggestions, #search { + margin-bottom: 2.5em; +} +//--> +</style> +</head> + +<body> + +<h1 jscontent="heading"></h1> + +<div id="errorSummary" jsselect="summary"> + <p jseval="this.innerHTML = $this.msg;"></p> +</div> + +<div id="suggestions"> + <h2 jscontent="suggestionsHeading"></h2> + <ul> + <li jsselect="suggestionsReload"> + <span jseval="this.innerHTML = $this.msg;"></span> + </li> + <li jsselect="suggestionsCache"> + <span jseval="this.innerHTML = $this.msg;"></span> + </li> + <li jsselect="suggestionsHomepage"> + <span jscontent="suggestionsHomepageMsg"></span> + <a jscontent="hostName" jsvalues="href:homePage"></a> + </li> + </ul> +</div> + +</body> +</html> diff --git a/chrome/renderer/resources/insecure_content_stamp.png b/chrome/renderer/resources/insecure_content_stamp.png Binary files differnew file mode 100644 index 0000000..1eb8dcb --- /dev/null +++ b/chrome/renderer/resources/insecure_content_stamp.png diff --git a/chrome/renderer/resources/neterror.html b/chrome/renderer/resources/neterror.html new file mode 100644 index 0000000..bf234f0 --- /dev/null +++ b/chrome/renderer/resources/neterror.html @@ -0,0 +1,104 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html id="t" jsvalues="dir:textdirection"> +<head> +<title jscontent="title"> +</title> +<style type="text/css"><!-- +body { + background-color: #fff; + color: #000; + font-family: arial,sans-serif; + font-size: 83%; + line-height: 120%; + max-width: 35em; + padding: 0.5em 1em; +} +li { + padding-bottom: .3em; +} +ul { + margin: .5em 0 0; + padding-bottom: 0; +} +h1 { + font-size: 1.5em; + margin-bottom: 1.5em; +} +h2 { + font-size: 1em; + font-weight: bold; + margin: 0 0 .5em; + padding: 0; +} +a { + color: #00c; +} +a:active { + color: #f00; +} +a:visited { + color: #551a8b; +} +#errorSummary, #suggestions, #search { + margin-bottom: 2.5em; +} +#zipInfo { + display: none; + padding-left: 16px; +} +#plus { + border: 0; + cursor: pointer; + vertical-align: baseline; +} +#details { + background-color: #e0e0e0; + max-width: 30em; + padding: 1em; +} +//--> +</style> +<script> +function toggleDiv(id) { + var elt = document.getElementById(id); + elt.style.display = (elt.style.display == 'block') ? 'none' : 'block'; +} +</script> +</head> + +<body> + +<h1 jscontent="heading"></h1> + +<div id="errorSummary" jsselect="summary"> + <p jseval="this.innerHTML = $this.msg;"></p> +</div> + +<div id="suggestions"> + <h2 jscontent="suggestionsHeading"></h2> + <ul> + <li jsselect="suggestionsReload"> + <span jseval="this.innerHTML = $this.msg;"></span> + </li> + <li jsselect="suggestionsCache"> + <span jseval="this.innerHTML = $this.msg;"></span> + </li> + <li jsselect="suggestionsHomepage"> + <span jscontent="suggestionsHomepageMsg"></span> + <a jscontent="hostName" jsvalues="href:homePage"></a> + </li> + </ul> +</div> + +<div> + <a href="javascript:void(0);" style="text-decoration:none" onclick="toggleDiv('zipInfo')"> + <img id="plus" src="data:image/png;base64,R0lGODlhDAAMAMQAAAAAzOjp7lVmrezt8Zmlztja4c/R2Gx9te/x9srN1PHz9+7w9Ofp7eHj5/f4+vDx89bZ3t3g5eTn6/P19/v8+/////39/fj4+P///wAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABgALAAAAAAMAAwAAAVFYEUIZEkSolCt7CocKgsALdnOrGXLdEVdJMdsOHyQJkTigLRAKBQzxWIQIDGugRlD0miQIoXwDEKGCEaGhHqdeKVMJVQIADs="> + <span id="errorExpander" jscontent="detailsLink" style="text-decoration:underline"></span> + </a> + <div id="zipInfo"> + <p jscontent="detailsHeading"></p> + <div id="details" jscontent="details"></div> + </div> +</div> +</body> +</html> diff --git a/chrome/renderer/spellcheck_unittest.cc b/chrome/renderer/spellcheck_unittest.cc new file mode 100644 index 0000000..3ef6c08 --- /dev/null +++ b/chrome/renderer/spellcheck_unittest.cc @@ -0,0 +1,360 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "webkit/glue/webkit_glue.h" + +#include "base/file_util.h" +#include "base/path_service.h" +#include "chrome/browser/spellchecker.h" +#include "chrome/common/chrome_paths.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +class SpellCheckTest : public testing::Test { +}; +} // namespace + +// Represents a special initialization function used only for the unit tests +// in this file. +extern void InitHunspellWithFiles(FILE* file_aff_hunspell, + FILE* file_dic_hunspell); + +// Operates unit tests for the webkit_glue::SpellCheckWord() function +// with the US English dictionary. +// The unit tests in this function consist of: +// * Tests for the function with empty strings; +// * Tests for the function with a valid English word; +// * Tests for the function with a valid non-English word; +// * Tests for the function with a valid English word with a preceding +// space character; +// * Tests for the function with a valid English word with a preceding +// non-English word; +// * Tests for the function with a valid English word with a following +// space character; +// * Tests for the function with a valid English word with a following +// non-English word; +// * Tests for the function with two valid English words concatenated +// with space characters or non-English words; +// * Tests for the function with an invalid English word; +// * Tests for the function with an invalid English word with a preceding +// space character; +// * Tests for the function with an invalid English word with a preceding +// non-English word; +// * Tests for the function with2 an invalid English word with a following +// space character; +// * Tests for the function with an invalid English word with a following +// non-English word, and; +// * Tests for the function with two invalid English words concatenated +// with space characters or non-English words. +// A test with a "[ROBUSTNESS]" mark shows it is a robustness test and it uses +// grammartically incorrect string. +// TODO(hbono): Please feel free to add more tests. +TEST(SpellCheckTest, SpellCheckStrings_EN_US) { + static const struct { + // A string to be tested. + const wchar_t* input; + // An expected result for this test case. + // * true: the input string does not have any invalid words. + // * false: the input string has one or more invalid words. + bool expected_result; + // The position and the length of the first invalid word. + int misspelling_start; + int misspelling_length; + } kTestCases[] = { + // Empty strings. + {NULL, true, 0, 0}, + {L"", true, 0, 0}, + {L" ", true, 0, 0}, + {L"\xA0", true, 0, 0}, + {L"\x3000", true, 0, 0}, + + // A valid English word "hello". + {L"hello", true, 0, 0}, + // A valid Chinese word (meaning "hello") consisiting of two CJKV + // ideographs + {L"\x4F60\x597D", true, 0, 0}, + // A valid Korean word (meaning "hello") consisting of five hangul + // syllables + {L"\xC548\xB155\xD558\xC138\xC694", true, 0, 0}, + // A valid Japanese word (meaning "hello") consisting of five Hiragana + // letters + {L"\x3053\x3093\x306B\x3061\x306F", true, 0, 0}, + // A valid Hindi word (meaning ?) consisting of six Devanagari letters + // (This word is copied from "http://b/issue?id=857583".) + {L"\x0930\x093E\x091C\x0927\x093E\x0928", true, 0, 0}, + // A valid English word "affix" using a Latin ligature 'ffi' + {L"a\xFB03x", true, 0, 0}, + // A valid English word "hello" (fullwidth version) + {L"\xFF28\xFF45\xFF4C\xFF4C\xFF4F", true, 0, 0}, + // Two valid Greek words (meaning "hello") consisting of seven Greek + // letters + {L"\x03B3\x03B5\x03B9\x03AC" L" " L"\x03C3\x03BF\x03C5", true, 0, 0}, + // A valid Russian word (meainng "hello") consisting of twelve Cyrillic + // letters + {L"\x0437\x0434\x0440\x0430\x0432\x0441" + L"\x0442\x0432\x0443\x0439\x0442\x0435", true, 0, 0}, + // A valid English contraction + {L"isn't", true, 0, 0}, + // A valid English word enclosed with underscores. + {L"_hello_", true, 0, 0}, + + // A valid English word with a preceding whitespace + {L" " L"hello", true, 0, 0}, + // A valid English word with a preceding no-break space + {L"\xA0" L"hello", true, 0, 0}, + // A valid English word with a preceding ideographic space + {L"\x3000" L"hello", true, 0, 0}, + // A valid English word with a preceding Chinese word + {L"\x4F60\x597D" L"hello", true, 0, 0}, + // [ROBUSTNESS] A valid English word with a preceding Korean word + {L"\xC548\xB155\xD558\xC138\xC694" L"hello", true, 0, 0}, + // A valid English word with a preceding Japanese word + {L"\x3053\x3093\x306B\x3061\x306F" L"hello", true, 0, 0}, + // [ROBUSTNESS] A valid English word with a preceding Hindi word + {L"\x0930\x093E\x091C\x0927\x093E\x0928" L"hello", true, 0, 0}, + // [ROBUSTNESS] A valid English word with two preceding Greek words + {L"\x03B3\x03B5\x03B9\x03AC" L" " L"\x03C3\x03BF\x03C5" + L"hello", true, 0, 0}, + // [ROBUSTNESS] A valid English word with a preceding Russian word + {L"\x0437\x0434\x0440\x0430\x0432\x0441" + L"\x0442\x0432\x0443\x0439\x0442\x0435" L"hello", true, 0, 0}, + + // A valid English word with a following whitespace + {L"hello" L" ", true, 0, 0}, + // A valid English word with a following no-break space + {L"hello" L"\xA0", true, 0, 0}, + // A valid English word with a following ideographic space + {L"hello" L"\x3000", true, 0, 0}, + // A valid English word with a following Chinese word + {L"hello" L"\x4F60\x597D", true, 0, 0}, + // [ROBUSTNESS] A valid English word with a following Korean word + {L"hello" L"\xC548\xB155\xD558\xC138\xC694", true, 0, 0}, + // A valid English word with a following Japanese word + {L"hello" L"\x3053\x3093\x306B\x3061\x306F", true, 0, 0}, + // [ROBUSTNESS] A valid English word with a following Hindi word + {L"hello" L"\x0930\x093E\x091C\x0927\x093E\x0928", true, 0, 0}, + // [ROBUSTNESS] A valid English word with two following Greek words + {L"hello" + L"\x03B3\x03B5\x03B9\x03AC" L" " L"\x03C3\x03BF\x03C5", true, 0, 0}, + // [ROBUSTNESS] A valid English word with a following Russian word + {L"hello" L"\x0437\x0434\x0440\x0430\x0432\x0441" + L"\x0442\x0432\x0443\x0439\x0442\x0435", true, 0, 0}, + + // Two valid English words concatenated with a whitespace + {L"hello" L" " L"hello", true, 0, 0}, + // Two valid English words concatenated with a no-break space + {L"hello" L"\xA0" L"hello", true, 0, 0}, + // Two valid English words concatenated with an ideographic space + {L"hello" L"\x3000" L"hello", true, 0, 0}, + // Two valid English words concatenated with a Chinese word + {L"hello" L"\x4F60\x597D" L"hello", true, 0, 0}, + // [ROBUSTNESS] Two valid English words concatenated with a Korean word + {L"hello" L"\xC548\xB155\xD558\xC138\xC694" L"hello", true, 0, 0}, + // Two valid English words concatenated with a Japanese word + {L"hello" L"\x3053\x3093\x306B\x3061\x306F" L"hello", true, 0, 0}, + // [ROBUSTNESS] Two valid English words concatenated with a Hindi word + {L"hello" L"\x0930\x093E\x091C\x0927\x093E\x0928" L"hello" , true, 0, 0}, + // [ROBUSTNESS] Two valid English words concatenated with two Greek words + {L"hello" L"\x03B3\x03B5\x03B9\x03AC" L" " L"\x03C3\x03BF\x03C5" + L"hello", true, 0, 0}, + // [ROBUSTNESS] Two valid English words concatenated with a Russian word + {L"hello" L"\x0437\x0434\x0440\x0430\x0432\x0441" + L"\x0442\x0432\x0443\x0439\x0442\x0435" L"hello", true, 0, 0}, + // [ROBUSTNESS] Two valid English words concatenated with a contraction + // character. + {L"hello:hello", true, 0, 0}, + + // An invalid English word + {L"ifmmp", false, 0, 5}, + // An invalid English word "bffly" containing a Latin ligature 'ffl' + {L"b\xFB04y", false, 0, 3}, + // An invalid English word "ifmmp" (fullwidth version) + {L"\xFF29\xFF46\xFF4D\xFF4D\xFF50", false, 0, 5}, + // An invalid English contraction + {L"jtm'u", false, 0, 5}, + // An invalid English word enclosed with underscores. + {L"_ifmmp_", false, 1, 5}, + + // An invalid English word with a preceding whitespace + {L" " L"ifmmp", false, 1, 5}, + // An invalid English word with a preceding no-break space + {L"\xA0" L"ifmmp", false, 1, 5}, + // An invalid English word with a preceding ideographic space + {L"\x3000" L"ifmmp", false, 1, 5}, + // An invalid English word with a preceding Chinese word + {L"\x4F60\x597D" L"ifmmp", false, 2, 5}, + // [ROBUSTNESS] An invalid English word with a preceding Korean word + {L"\xC548\xB155\xD558\xC138\xC694" L"ifmmp", false, 5, 5}, + // An invalid English word with a preceding Japanese word + {L"\x3053\x3093\x306B\x3061\x306F" L"ifmmp", false, 5, 5}, + // [ROBUSTNESS] An invalid English word with a preceding Hindi word + {L"\x0930\x093E\x091C\x0927\x093E\x0928" L"ifmmp", false, 6, 5}, + // [ROBUSTNESS] An invalid English word with two preceding Greek words + {L"\x03B3\x03B5\x03B9\x03AC" L" " L"\x03C3\x03BF\x03C5" + L"ifmmp", false, 8, 5}, + // [ROBUSTNESS] An invalid English word with a preceding Russian word + {L"\x0437\x0434\x0440\x0430\x0432\x0441" + L"\x0442\x0432\x0443\x0439\x0442\x0435" L"ifmmp", false, 12, 5}, + + // An invalid English word with a following whitespace + {L"ifmmp" L" ", false, 0, 5}, + // An invalid English word with a following no-break space + {L"ifmmp" L"\xA0", false, 0, 5}, + // An invalid English word with a following ideographic space + {L"ifmmp" L"\x3000", false, 0, 5}, + // An invalid English word with a following Chinese word + {L"ifmmp" L"\x4F60\x597D", false, 0, 5}, + // [ROBUSTNESS] An invalid English word with a following Korean word + {L"ifmmp" L"\xC548\xB155\xD558\xC138\xC694", false, 0, 5}, + // An invalid English word with a following Japanese word + {L"ifmmp" L"\x3053\x3093\x306B\x3061\x306F", false, 0, 5}, + // [ROBUSTNESS] An invalid English word with a following Hindi word + {L"ifmmp" L"\x0930\x093E\x091C\x0927\x093E\x0928", false, 0, 5}, + // [ROBUSTNESS] An invalid English word with two following Greek words + {L"ifmmp" + L"\x03B3\x03B5\x03B9\x03AC" L" " L"\x03C3\x03BF\x03C5", false, 0, 5}, + // [ROBUSTNESS] An invalid English word with a following Russian word + {L"ifmmp" L"\x0437\x0434\x0440\x0430\x0432\x0441" + L"\x0442\x0432\x0443\x0439\x0442\x0435", false, 0, 5}, + + // Two invalid English words concatenated with a whitespace + {L"ifmmp" L" " L"ifmmp", false, 0, 5}, + // Two invalid English words concatenated with a no-break space + {L"ifmmp" L"\xA0" L"ifmmp", false, 0, 5}, + // Two invalid English words concatenated with an ideographic space + {L"ifmmp" L"\x3000" L"ifmmp", false, 0, 5}, + // Two invalid English words concatenated with a Chinese word + {L"ifmmp" L"\x4F60\x597D" L"ifmmp", false, 0, 5}, + // [ROBUSTNESS] Two invalid English words concatenated with a Korean word + {L"ifmmp" L"\xC548\xB155\xD558\xC138\xC694" L"ifmmp", false, 0, 5}, + // Two invalid English words concatenated with a Japanese word + {L"ifmmp" L"\x3053\x3093\x306B\x3061\x306F" L"ifmmp", false, 0, 5}, + // [ROBUSTNESS] Two invalid English words concatenated with a Hindi word + {L"ifmmp" L"\x0930\x093E\x091C\x0927\x093E\x0928" L"ifmmp" , false, 0, 5}, + // [ROBUSTNESS] Two invalid English words concatenated with two Greek words + {L"ifmmp" L"\x03B3\x03B5\x03B9\x03AC" L" " L"\x03C3\x03BF\x03C5" + L"ifmmp", false, 0, 5}, + // [ROBUSTNESS] Two invalid English words concatenated with a Russian word + {L"ifmmp" L"\x0437\x0434\x0440\x0430\x0432\x0441" + L"\x0442\x0432\x0443\x0439\x0442\x0435" L"ifmmp", false, 0, 5}, + // [ROBUSTNESS] Two invalid English words concatenated with a contraction + // character. + {L"ifmmp:ifmmp", false, 0, 11}, + }; + + std::wstring hunspell_directory; + ASSERT_TRUE(PathService::Get(chrome::DIR_APP_DICTIONARIES, + &hunspell_directory)); + + scoped_refptr<SpellChecker> spell_checker(new SpellChecker( + hunspell_directory, L"en-US", NULL)); + + for (int i = 0; i < arraysize(kTestCases); i++) { + size_t input_length = 0; + if (kTestCases[i].input != NULL) { + input_length = wcslen(kTestCases[i].input); + } + int misspelling_start; + int misspelling_length; + bool result = spell_checker->SpellCheckWord(kTestCases[i].input, + static_cast<int>(input_length), + &misspelling_start, + &misspelling_length, NULL); + + EXPECT_EQ(result, kTestCases[i].expected_result); + EXPECT_EQ(misspelling_start, kTestCases[i].misspelling_start); + EXPECT_EQ(misspelling_length, kTestCases[i].misspelling_length); + } +} + + +TEST(SpellCheckTest, SpellCheckSuggestions_EN_US) { + static const struct { + // A string to be tested. + const wchar_t* input; + // An expected result for this test case. + // * true: the input string does not have any invalid words. + // * false: the input string has one or more invalid words. + bool expected_result; + // The position and the length of the first invalid word. + int misspelling_start; + int misspelling_length; + + // A suggested word that should occur. + const wchar_t* suggested_word; + } kTestCases[] = { // A valid English word with a preceding whitespace + {L"ello", false, 0, 0, L"hello"}, + {L"ello", false, 0, 0, L"cello"}, + {L"wate", false, 0, 0, L"water"}, + {L"wate", false, 0, 0, L"waste"}, + {L"wate", false, 0, 0, L"sate"}, + {L"wate", false, 0, 0, L"rate"}, + {L"jum", false, 0, 0, L"jump"}, + {L"jum", false, 0, 0, L"rum"}, + {L"jum", false, 0, 0, L"sum"}, + {L"jum", false, 0, 0, L"tum"}, + // TODO (Sidchat): add many more examples. + }; + + std::wstring hunspell_directory; + ASSERT_TRUE(PathService::Get(chrome::DIR_APP_DICTIONARIES, + &hunspell_directory)); + + scoped_refptr<SpellChecker> spell_checker(new SpellChecker( + hunspell_directory, L"en-US", NULL)); + + for (int i = 0; i < arraysize(kTestCases); i++) { + std::vector<std::wstring> suggestions; + size_t input_length = 0; + if (kTestCases[i].input != NULL) { + input_length = wcslen(kTestCases[i].input); + } + int misspelling_start; + int misspelling_length; + bool result = spell_checker->SpellCheckWord(kTestCases[i].input, + static_cast<int>(input_length), + &misspelling_start, + &misspelling_length, + &suggestions); + + // Check for spelling. + EXPECT_EQ(result, kTestCases[i].expected_result); + + // Check if the suggested words occur. + bool suggested_word_is_present = false; + for (int j=0; j < static_cast<int>(suggestions.size()); j++) { + if (suggestions.at(j).compare(kTestCases[i].suggested_word) == 0) { + suggested_word_is_present = true; + break; + } + } + + EXPECT_TRUE(suggested_word_is_present); + } +} diff --git a/chrome/renderer/visitedlink_slave.cc b/chrome/renderer/visitedlink_slave.cc new file mode 100644 index 0000000..80741e0 --- /dev/null +++ b/chrome/renderer/visitedlink_slave.cc @@ -0,0 +1,88 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <windows.h> +#include "base/logging.h" +#include "base/shared_memory.h" +#include "chrome/common/win_util.h" +#include "chrome/renderer/visitedlink_slave.h" + +VisitedLinkSlave::VisitedLinkSlave() : shared_memory_(NULL) { +} +VisitedLinkSlave::~VisitedLinkSlave() { + FreeTable(); +} + +// This function's job is to initialize the table with the given +// shared memory handle. This memory is mappend into the process. +bool VisitedLinkSlave::Init(SharedMemoryHandle shared_memory) { + // since this function may be called again to change the table, we may need + // to free old objects + FreeTable(); + DCHECK(shared_memory_ == NULL && hash_table_ == NULL); + + // create the shared memory object + shared_memory_ = new SharedMemory(shared_memory, true); + if (!shared_memory_) + return false; + + // map the header into our process so we can see how long the rest is, + // and set the salt + if (!shared_memory_->Map(sizeof(SharedHeader))) + return false; + SharedHeader* header = + static_cast<SharedHeader*>(shared_memory_->memory()); + DCHECK(header); + int32 table_len = header->length; + memcpy(salt_, header->salt, sizeof(salt_)); + shared_memory_->Unmap(); + + // now do the whole table because we know the length + if (!shared_memory_->Map(sizeof(SharedHeader) + + table_len * sizeof(Fingerprint))) { + shared_memory_->Close(); + return false; + } + + // commit the data + DCHECK(shared_memory_->memory()); + hash_table_ = reinterpret_cast<Fingerprint*>( + static_cast<char*>(shared_memory_->memory()) + sizeof(SharedHeader)); + table_length_ = table_len; + return true; +} + +void VisitedLinkSlave::FreeTable() { + if (shared_memory_ ) { + delete shared_memory_; + shared_memory_ = NULL; + } + hash_table_ = NULL; + table_length_ = 0; +} diff --git a/chrome/renderer/visitedlink_slave.h b/chrome/renderer/visitedlink_slave.h new file mode 100644 index 0000000..24c708e --- /dev/null +++ b/chrome/renderer/visitedlink_slave.h @@ -0,0 +1,62 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_WIN_RENDERER_VISITEDLINK_SLAVE_H__ +#define CHROME_WIN_RENDERER_VISITEDLINK_SLAVE_H__ + +#include <string> +#include <windows.h> + +#include "base/shared_memory.h" +#include "chrome/common/visitedlink_common.h" + +// Reads the link coloring database provided by the master. There can be any +// number of slaves reading the same database. +class VisitedLinkSlave : public VisitedLinkCommon +{ + public: + VisitedLinkSlave(); + virtual ~VisitedLinkSlave(); + + // Called to initialize this object, nothing will work until this is called. + // It can also be called again at any time to update the table that we're + // using. The handle should be the handle generated by the VisitedLinkMaster. + // Returns true on success. + bool Init(SharedMemoryHandle shared_memory); + + private: + void FreeTable(); + + // shared memory consists of a SharedHeader followed by the table + SharedMemory* shared_memory_; + + DISALLOW_EVIL_CONSTRUCTORS(VisitedLinkSlave); +}; + +#endif // WIN_RENDERER_VISITEDLINK_SLAVE_H__
\ No newline at end of file diff --git a/chrome/renderer/webplugin_delegate_proxy.cc b/chrome/renderer/webplugin_delegate_proxy.cc new file mode 100644 index 0000000..0dc1436 --- /dev/null +++ b/chrome/renderer/webplugin_delegate_proxy.cc @@ -0,0 +1,696 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/renderer/webplugin_delegate_proxy.h" + +#include <atlbase.h> + +#include "generated_resources.h" + +#include "base/logging.h" +#include "base/ref_counted.h" +#include "base/string_util.h" +#include "base/gfx/size.h" + +#include "chrome/app/chrome_dll_resource.h" +#include "chrome/common/gfx/chrome_canvas.h" +#include "chrome/common/gfx/emf.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/common/win_util.h" +#include "chrome/plugin/npobject_proxy.h" +#include "chrome/plugin/npobject_stub.h" +#include "chrome/renderer/render_thread.h" +#include "chrome/renderer/render_view.h" +#include "googleurl/src/gurl.h" +#include "net/base/mime_util.h" +#include "webkit/glue/glue_util.h" +#include "webkit/glue/webframe.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/webview.h" + +// Proxy for WebPluginResourceClient. The object owns itself after creation, +// deleting itself after its callback has been called. +class ResourceClientProxy : public WebPluginResourceClient { + public: + ResourceClientProxy(PluginChannelHost* channel, int instance_id) + : channel_(channel), instance_id_(instance_id), resource_id_(0), + notify_needed_(false), notify_data_(NULL) { + } + + ~ResourceClientProxy() { + } + + void Initialize(int resource_id, const std::string &url, bool notify_needed, + void *notify_data) { + resource_id_ = resource_id; + url_ = url; + notify_needed_ = notify_needed; + notify_data_ = notify_data; + + PluginMsg_URLRequestReply_Params params; + params.resource_id = resource_id; + params.url = url_; + params.notify_needed = notify_needed_; + params.notify_data = notify_data_; + + channel_->Send(new PluginMsg_HandleURLRequestReply(instance_id_, params)); + } + + // PluginResourceClient implementation: + void WillSendRequest(const GURL& url) { + DCHECK(channel_ != NULL); + channel_->Send(new PluginMsg_WillSendRequest(instance_id_, resource_id_, + url)); + } + + void DidReceiveResponse(const std::string& mime_type, + const std::string& headers, + uint32 expected_length, + uint32 last_modified, + bool* cancel) { + DCHECK(channel_ != NULL); + PluginMsg_DidReceiveResponseParams params; + params.id = resource_id_; + params.mime_type = mime_type; + params.headers = headers; + params.expected_length = expected_length; + params.last_modified = last_modified; + // Grab a reference on the underlying channel so it does not get + // deleted from under us. + scoped_refptr<PluginChannelHost> channel_ref(channel_); + channel_->Send(new PluginMsg_DidReceiveResponse(instance_id_, params, + cancel)); + } + + void DidReceiveData(const char* buffer, int length) { + DCHECK(channel_ != NULL); + DCHECK(length > 0); + std::vector<char> data; + data.resize(static_cast<size_t>(length)); + memcpy(&data.front(), buffer, length); + // Grab a reference on the underlying channel so it does not get + // deleted from under us. + scoped_refptr<PluginChannelHost> channel_ref(channel_); + channel_->Send(new PluginMsg_DidReceiveData(instance_id_, resource_id_, + data)); + } + + void DidFinishLoading() { + DCHECK(channel_ != NULL); + channel_->Send(new PluginMsg_DidFinishLoading(instance_id_, resource_id_)); + channel_ = NULL; + MessageLoop::current()->DeleteSoon(FROM_HERE, this); + } + + void DidFail() { + DCHECK(channel_ != NULL); + channel_->Send(new PluginMsg_DidFail(instance_id_, resource_id_)); + channel_ = NULL; + MessageLoop::current()->DeleteSoon(FROM_HERE, this); + } + + private: + int resource_id_; + int instance_id_; + scoped_refptr<PluginChannelHost> channel_; + std::string url_; + bool notify_needed_; + void* notify_data_; +}; + +WebPluginDelegateProxy* WebPluginDelegateProxy::Create( + const GURL& url, + const std::string& mime_type, + const std::string& clsid, + RenderView* render_view) { + return new WebPluginDelegateProxy(mime_type, clsid, render_view); +} + +WebPluginDelegateProxy::WebPluginDelegateProxy(const std::string& mime_type, + const std::string& clsid, + RenderView* render_view) + : render_view_(render_view), + mime_type_(mime_type), + clsid_(clsid), + plugin_(NULL), + windowless_(false), + first_paint_(true), + npobject_(NULL), + send_deferred_update_geometry_(false), + visible_(false), + sad_plugin_(NULL), + window_script_object_(NULL), + modal_loop_pump_messages_event_(NULL) { +} + +WebPluginDelegateProxy::~WebPluginDelegateProxy() { + if (npobject_) + NPN_ReleaseObject(npobject_); + + if (window_script_object_) { + window_script_object_->set_proxy(NULL); + window_script_object_->set_invalid(); + } + + if (modal_loop_pump_messages_event_) { + CloseHandle(modal_loop_pump_messages_event_); + } +} + +void WebPluginDelegateProxy::PluginDestroyed() { + plugin_ = NULL; + + if (channel_host_) { + if (npobject_) { + // When we destroy the plugin instance, the NPObjectStub NULLs out its + // pointer to the npobject (see NPObjectStub::OnChannelError). Therefore, + // we release the object before destroying the instance to avoid leaking. + NPN_ReleaseObject(npobject_); + npobject_ = NULL; + } + + channel_host_->RemoveRoute(instance_id_); + Send(new PluginMsg_DestroyInstance(instance_id_)); + channel_host_ = NULL; + } + render_view_->PluginDestroyed(this); + delete this; +} + +void WebPluginDelegateProxy::FlushGeometryUpdates() { + if (send_deferred_update_geometry_) { + send_deferred_update_geometry_ = false; + Send(new PluginMsg_UpdateGeometry(instance_id_, + plugin_rect_, + deferred_clip_rect_, + visible_)); + } +} + +bool WebPluginDelegateProxy::Initialize(const GURL& url, char** argn, + char** argv, int argc, + WebPlugin* plugin, + bool load_manually) { + std::wstring channel_name, plugin_path; + if (!RenderThread::current()->Send(new ViewHostMsg_OpenChannelToPlugin( + url, mime_type_, clsid_, webkit_glue::GetWebKitLocale(), + &channel_name, &plugin_path))) + return false; + + MessageLoop* ipc_message_loop = RenderThread::current()->owner_loop(); + scoped_refptr<PluginChannelHost> channel_host = + PluginChannelHost::GetPluginChannelHost(channel_name, ipc_message_loop); + if (!channel_host.get()) + return false; + + int instance_id; + bool result = channel_host->Send(new PluginMsg_CreateInstance( + mime_type_, &instance_id)); + if (!result) + return false; + + plugin_path_ = plugin_path; + channel_host_ = channel_host; + instance_id_ = instance_id; + + channel_host_->AddRoute(instance_id_, this, false); + + // Now tell the PluginInstance in the plugin process to initialize. + PluginMsg_Init_Params params; + params.containing_window = render_view_->host_window(); + params.url = url; + for (int i = 0; i < argc; ++i) { + params.arg_names.push_back(argn[i]); + params.arg_values.push_back(argv[i]); + } + params.load_manually = load_manually; + params.modal_dialog_event = render_view_->modal_dialog_event(); + + plugin_ = plugin; + + result = false; + IPC::Message* msg = new PluginMsg_Init(instance_id_, params, &result); + Send(msg); + + return result; +} + +bool WebPluginDelegateProxy::Send(IPC::Message* msg) { + if (!channel_host_) { + DLOG(WARNING) << "dropping message because channel host is null"; + delete msg; + return false; + } + + return channel_host_->Send(msg); +} + +void WebPluginDelegateProxy::SendJavaScriptStream(const std::string& url, + const std::wstring& result, + bool success, + bool notify_needed, + int notify_data) { + PluginMsg_SendJavaScriptStream* msg = + new PluginMsg_SendJavaScriptStream(instance_id_, url, result, + success, notify_needed, + notify_data); + Send(msg); +} + +void WebPluginDelegateProxy::DidReceiveManualResponse( + const std::string& url, const std::string& mime_type, + const std::string& headers, uint32 expected_length, + uint32 last_modified) { + PluginMsg_DidReceiveResponseParams params; + params.id = 0; + params.mime_type = mime_type; + params.headers = headers; + params.expected_length = expected_length; + params.last_modified = last_modified; + Send(new PluginMsg_DidReceiveManualResponse(instance_id_, url, params)); +} + +void WebPluginDelegateProxy::DidReceiveManualData(const char* buffer, + int length) { + DCHECK(length > 0); + std::vector<char> data; + data.resize(static_cast<size_t>(length)); + memcpy(&data.front(), buffer, length); + Send(new PluginMsg_DidReceiveManualData(instance_id_, data)); +} + +void WebPluginDelegateProxy::DidFinishManualLoading() { + Send(new PluginMsg_DidFinishManualLoading(instance_id_)); +} + +void WebPluginDelegateProxy::DidManualLoadFail() { + Send(new PluginMsg_DidManualLoadFail(instance_id_)); +} + +std::wstring WebPluginDelegateProxy::GetPluginPath() { + return plugin_path_; +} + +void WebPluginDelegateProxy::InstallMissingPlugin() { + Send(new PluginMsg_InstallMissingPlugin(instance_id_)); +} + +void WebPluginDelegateProxy::OnMessageReceived(const IPC::Message& msg) { + IPC_BEGIN_MESSAGE_MAP(WebPluginDelegateProxy, msg) + IPC_MESSAGE_HANDLER(PluginHostMsg_SetWindow, OnSetWindow) + IPC_MESSAGE_HANDLER(PluginHostMsg_CancelResource, OnCancelResource) + IPC_MESSAGE_HANDLER(PluginHostMsg_Invalidate, OnInvalidate) + IPC_MESSAGE_HANDLER(PluginHostMsg_InvalidateRect, OnInvalidateRect) + IPC_MESSAGE_HANDLER(PluginHostMsg_GetWindowScriptNPObject, + OnGetWindowScriptNPObject) + IPC_MESSAGE_HANDLER(PluginHostMsg_GetPluginElement, + OnGetPluginElement) + IPC_MESSAGE_HANDLER(PluginHostMsg_SetCookie, OnSetCookie) + IPC_MESSAGE_HANDLER(PluginHostMsg_GetCookies, OnGetCookies) + IPC_MESSAGE_HANDLER(PluginHostMsg_ShowModalHTMLDialog, + OnShowModalHTMLDialog) + IPC_MESSAGE_HANDLER(PluginHostMsg_MissingPluginStatus, + OnMissingPluginStatus) + IPC_MESSAGE_HANDLER(PluginHostMsg_URLRequest, OnHandleURLRequest) + IPC_MESSAGE_HANDLER(PluginHostMsg_GetCPBrowsingContext, + OnGetCPBrowsingContext) + IPC_MESSAGE_UNHANDLED_ERROR() + IPC_END_MESSAGE_MAP() +} + +void WebPluginDelegateProxy::OnChannelError() { + OnInvalidate(); + render_view_->PluginCrashed(plugin_path_); +} + +void WebPluginDelegateProxy::UpdateGeometry(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, + bool visible) { + plugin_rect_ = window_rect; + + if (windowless_) { + IPC::Message* msg = new PluginMsg_UpdateGeometry( + instance_id_, window_rect, clip_rect, visible); + msg->set_unblock(true); + Send(msg); + } else { + deferred_clip_rect_ = clip_rect; + visible_ = visible; + send_deferred_update_geometry_ = true; + } +} + +void WebPluginDelegateProxy::Paint(HDC hdc, const gfx::Rect& damaged_rect) { + // If the plugin is no longer connected (channel crashed) draw a crashed + // plugin bitmap + if (!channel_host_->channel_valid()) { + PaintSadPlugin(hdc, damaged_rect); + return; + } + + // Can't duplicate an HDC handle, so get all the parameters we need in + // order to recreate the same HDC in the plugin process. + PluginMsg_Paint_Params params; + HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); + if (bitmap == NULL) { + NOTREACHED(); + return; + } + + DIBSECTION dibsection = { 0 }; + int result = GetObject(bitmap, sizeof(dibsection), &dibsection); + if (!result) { + NOTREACHED(); + return; + } + + win_util::ScopedHRGN hrgn(CreateRectRgn(0, 0, 0, 0)); + result = GetClipRgn(hdc, hrgn); + if (result == -1) { + NOTREACHED(); + return; + } + + params.size.SetSize(dibsection.dsBmih.biWidth, dibsection.dsBmih.biHeight); + if (result == 0) { + // No clipping region. + params.clip_rect.set_width(params.size.width()); + params.clip_rect.set_height(params.size.height()); + } else { + RECT clip_rect; + result = GetRgnBox(hrgn, &clip_rect); + DCHECK_NE(result, 0); + params.clip_rect = clip_rect; + } + + if (!GetWorldTransform(hdc, ¶ms.xf)) { + NOTREACHED(); + return; + } + + params.damaged_rect = damaged_rect; + params.shared_memory = dibsection.dshSection; + + // Normal painting code path. + if (dibsection.dshSection) { + // No paint events for windowed plugins. However, if it is the first paint + // we don't know yet whether the plugin is windowless or not, so we have to + // send the event. + if (!windowless_ && !first_paint_) { + // TODO(maruel): That's not true for printing and thumbnail capture. + // We shall use PrintWindow() to draw the window. + return; + } + first_paint_ = false; + + DCHECK(params.size.width()); + DCHECK(params.size.height()); + Send(new PluginMsg_Paint(instance_id_, params)); + return; + } + + params.size.SetSize(damaged_rect.width(), damaged_rect.height()); + // Pass the window size instead. + if (!windowless_) + params.damaged_rect = plugin_rect_; + + // When the thumbnail is painted or during printing, a shared memory handle + // isn't given to CreateDIBSection so we don't get one now. + size_t bytes = 0; + SharedMemoryHandle emf_buffer = NULL; + PluginMsg_PaintIntoSharedMemory* msg = + new PluginMsg_PaintIntoSharedMemory(instance_id_, params, &emf_buffer, + &bytes); + if (!Send(msg)) + return; + + if (!emf_buffer) { + NOTREACHED(); + return; + } + + // memory's destructor will automatically close emf_buffer. + SharedMemory memory(emf_buffer, true); + if (!memory.Map(bytes)) { + NOTREACHED(); + return; + } + + gfx::Emf emf; + if (!emf.CreateFromData(memory.memory(), bytes)) { + NOTREACHED(); + return; + } + emf.Playback(hdc, NULL); +} + +void WebPluginDelegateProxy::Print(HDC hdc) { + PluginMsg_PrintResponse_Params params = { 0 }; + Send(new PluginMsg_Print(instance_id_, ¶ms)); + + SharedMemory memory(params.shared_memory, true); + if (!memory.Map(params.size)) { + NOTREACHED(); + return; + } + + gfx::Emf emf; + if (!emf.CreateFromData(memory.memory(), params.size)) { + NOTREACHED(); + return; + } + // Playback the buffer. + emf.Playback(hdc, NULL); +} + +NPObject* WebPluginDelegateProxy::GetPluginScriptableObject() { + if (npobject_) + return NPN_RetainObject(npobject_); + + int route_id = MSG_ROUTING_NONE; + void* npobject_ptr; + Send(new PluginMsg_GetPluginScriptableObject( + instance_id_, &route_id, &npobject_ptr)); + if (route_id == MSG_ROUTING_NONE) + return NULL; + + npobject_ = NPObjectProxy::Create( + channel_host_.get(), route_id, npobject_ptr, NULL); + + return NPN_RetainObject(npobject_); +} + +void WebPluginDelegateProxy::DidFinishLoadWithReason(NPReason reason) { + Send(new PluginMsg_DidFinishLoadWithReason(instance_id_, reason)); +} + +void WebPluginDelegateProxy::SetFocus() { + Send(new PluginMsg_SetFocus(instance_id_)); +} + +bool WebPluginDelegateProxy::HandleEvent(NPEvent* event, WebCursor* cursor) { + bool handled; + // A windowless plugin can enter a modal loop in the context of a + // NPP_HandleEvent call, in which case we need to pump messages to + // the plugin. We pass of the corresponding event handle to the + // plugin process, which is set if the plugin does enter a modal loop. + IPC::SyncMessage* message = new PluginMsg_HandleEvent(instance_id_, + *event, &handled, + cursor); + message->set_pump_messages_event(modal_loop_pump_messages_event_); + Send(message); + return handled; +} + +int WebPluginDelegateProxy::GetProcessId() { + return channel_host_->peer_pid(); +} + +HWND WebPluginDelegateProxy::GetWindowHandle() { + NOTREACHED() << "GetWindowHandle can't be called on the proxy."; + return NULL; +} + +void WebPluginDelegateProxy::OnSetWindow( + HWND window, HANDLE modal_loop_pump_messages_event) { + windowless_ = window == NULL; + if (plugin_) + plugin_->SetWindow(window, modal_loop_pump_messages_event); + + modal_loop_pump_messages_event_ = modal_loop_pump_messages_event; +} + +void WebPluginDelegateProxy::OnCancelResource(int id) { + if (plugin_) + plugin_->CancelResource(id); +} + +void WebPluginDelegateProxy::OnInvalidate() { + if (plugin_) + plugin_->Invalidate(); +} + +void WebPluginDelegateProxy::OnInvalidateRect(const gfx::Rect& rect) { + if (plugin_) + plugin_->InvalidateRect(rect); +} + +void WebPluginDelegateProxy::OnGetWindowScriptNPObject( + int route_id, bool* success, void** npobject_ptr) { + *success = false; + NPObject* npobject = NULL; + if (plugin_) + npobject = plugin_->GetWindowScriptNPObject(); + + if (!npobject) + return; + + // The stub will delete itself when the proxy tells it that it's released, or + // otherwise when the channel is closed. + NPObjectStub* stub = new NPObjectStub(npobject, channel_host_.get(), + route_id); + window_script_object_ = stub; + window_script_object_->set_proxy(this); + *success = true; + *npobject_ptr = npobject; +} + +void WebPluginDelegateProxy::OnGetPluginElement( + int route_id, bool* success, void** npobject_ptr) { + *success = false; + NPObject* npobject = NULL; + if (plugin_) + npobject = plugin_->GetPluginElement(); + if (!npobject) + return; + + // The stub will delete itself when the proxy tells it that it's released, or + // otherwise when the channel is closed. + NPObjectStub* stub = new NPObjectStub(npobject, channel_host_.get(), + route_id); + *success = true; + *npobject_ptr = npobject; +} + +void WebPluginDelegateProxy::OnSetCookie(const GURL& url, + const GURL& policy_url, + const std::string& cookie) { + if (plugin_) + plugin_->SetCookie(url, policy_url, cookie); +} + +void WebPluginDelegateProxy::OnGetCookies(const GURL& url, + const GURL& policy_url, + std::string* cookies) { + DCHECK(cookies); + if (plugin_) + *cookies = plugin_->GetCookies(url, policy_url); +} + +void WebPluginDelegateProxy::OnShowModalHTMLDialog( + const GURL& url, int width, int height, const std::string& json_arguments, + std::string* json_retval) { + DCHECK(json_retval); + if (render_view_) + render_view_->ShowModalHTMLDialog(url, width, height, json_arguments, + json_retval); +} + +void WebPluginDelegateProxy::OnMissingPluginStatus(int status) { + if (render_view_) + render_view_->OnMissingPluginStatus(this, status); +} + +void WebPluginDelegateProxy::OnGetCPBrowsingContext(uint32* context) { + *context = render_view_ ? render_view_->GetCPBrowsingContext() : 0; +} + +void WebPluginDelegateProxy::PaintSadPlugin(HDC hdc, const gfx::Rect& rect) { + const int width = plugin_rect_.width(); + const int height = plugin_rect_.height(); + + ChromeCanvas canvas(width, height, false); + SkPaint paint; + + paint.setStyle(SkPaint::kFill_Style); + paint.setColor(SK_ColorBLACK); + canvas.drawRectCoords(0, 0, SkIntToScalar(width), SkIntToScalar(height), + paint); + + if (!sad_plugin_) { + sad_plugin_ = ResourceBundle::LoadBitmap( + _AtlBaseModule.GetResourceInstance(), IDR_CRASHED_PLUGIN); + } + + if (sad_plugin_) { + canvas.DrawBitmapInt(*sad_plugin_, + std::max(0, (width - sad_plugin_->width())/2), + std::max(0, (height - sad_plugin_->height())/2)); + } + + canvas.getTopPlatformDevice().drawToHDC( + hdc, plugin_rect_.x(), plugin_rect_.y(), NULL); + return; +} + +void WebPluginDelegateProxy::OnHandleURLRequest( + const PluginHostMsg_URLRequest_Params& params) { + const char* data = NULL; + if (params.buffer.size()) + data = ¶ms.buffer[0]; + + const char* target = NULL; + if (params.target.length()) + target = params.target.c_str(); + + plugin_->HandleURLRequest(params.method.c_str(), + params.is_javascript_url, target, + static_cast<unsigned int>(params.buffer.size()), + data, params.is_file_data, params.notify, + params.url.c_str(), params.notify_data, + params.popups_allowed); +} + +WebPluginResourceClient* WebPluginDelegateProxy::CreateResourceClient( + int resource_id, const std::string &url, bool notify_needed, + void *notify_data) { + ResourceClientProxy* proxy = new ResourceClientProxy(channel_host_, + instance_id_); + proxy->Initialize(resource_id, url, notify_needed, notify_data); + return proxy; +} + +void WebPluginDelegateProxy::URLRequestRouted(const std::string& url, + bool notify_needed, + void* notify_data) { + Send(new PluginMsg_URLRequestRouted(instance_id_, url, notify_needed, + notify_data)); +} diff --git a/chrome/renderer/webplugin_delegate_proxy.h b/chrome/renderer/webplugin_delegate_proxy.h new file mode 100644 index 0000000..b13cc76 --- /dev/null +++ b/chrome/renderer/webplugin_delegate_proxy.h @@ -0,0 +1,175 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CHROME_RENDERER_WEBPLUGIN_DELEGATE_PROXY_H__ +#define CHROME_RENDERER_WEBPLUGIN_DELEGATE_PROXY_H__ + +#include <set> +#include <string> + +#include "base/gfx/rect.h" +#include "base/ref_counted.h" +#include "chrome/common/ipc_message.h" +#include "chrome/common/plugin_messages.h" +#include "chrome/plugin/npobject_stub.h" +#include "chrome/renderer/plugin_channel_host.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/webplugin_delegate.h" + +class GURL; +struct PluginHostMsg_RouteToFrame_Params; +class RenderView; +class SkBitmap; + +// An implementation of WebPluginDelegate that proxies all calls to +// the plugin process. +class WebPluginDelegateProxy : public WebPluginDelegate, + public IPC::Channel::Listener, + public IPC::Message::Sender { + public: + static WebPluginDelegateProxy* Create(const GURL& url, + const std::string& mime_type, + const std::string& clsid, + RenderView* render_view); + + // Called to drop our back-pointer to the containing RenderView. + void DropRenderView() { render_view_ = NULL; } + + // Called to drop our pointer to the window script object. + void DropWindowScriptObject() { window_script_object_ = NULL; } + + // Called to flush any deferred geometry changes to the plugin process. + virtual void FlushGeometryUpdates(); + + // WebPluginDelegate implementation: + virtual void PluginDestroyed(); + virtual bool Initialize(const GURL& url, char** argn, char** argv, int argc, + WebPlugin* plugin, bool load_manually); + virtual void UpdateGeometry(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect, bool visible); + virtual void Paint(HDC hdc, const gfx::Rect& rect); + virtual void Print(HDC hdc); + virtual NPObject* GetPluginScriptableObject(); + virtual void DidFinishLoadWithReason(NPReason reason); + virtual void SetFocus(); + virtual bool HandleEvent(NPEvent* event, WebCursor* cursor); + virtual int GetProcessId(); + virtual HWND GetWindowHandle(); + + // IPC::Channel::Listener implementation: + virtual void OnMessageReceived(const IPC::Message& msg); + void OnChannelError(); + + // IPC::Message::Sender implementation: + virtual bool Send(IPC::Message* msg); + + virtual void SendJavaScriptStream(const std::string& url, + const std::wstring& result, + bool success, bool notify_needed, + int notify_data); + + virtual void DidReceiveManualResponse(const std::string& url, + const std::string& mime_type, + const std::string& headers, + uint32 expected_length, + uint32 last_modified); + virtual void DidReceiveManualData(const char* buffer, int length); + virtual void DidFinishManualLoading(); + virtual void DidManualLoadFail(); + virtual std::wstring GetPluginPath(); + virtual void InstallMissingPlugin(); + virtual WebPluginResourceClient* CreateResourceClient(int resource_id, + const std::string &url, + bool notify_needed, + void *notify_data); + + // Notifies the delegate about a Get/Post URL request getting routed + virtual void URLRequestRouted(const std::string&url, bool notify_needed, + void* notify_data); + + private: + WebPluginDelegateProxy(const std::string& mime_type, + const std::string& clsid, + RenderView* render_view); + ~WebPluginDelegateProxy(); + + // Message handlers for messages that proxy WebPlugin methods, which + // we translate into calls to the real WebPlugin. + void OnSetWindow(HWND window, HANDLE modal_loop_pump_messages_event); + void OnCompleteURL(const std::string& url_in, std::string* url_out, + bool* result); + void OnHandleURLRequest(const PluginHostMsg_URLRequest_Params& params); + void OnCancelResource(int id); + void OnInvalidate(); + void OnInvalidateRect(const gfx::Rect& rect); + void OnGetWindowScriptNPObject(int route_id, bool* success, void** npobject_ptr); + void OnGetPluginElement(int route_id, bool* success, void** npobject_ptr); + void OnSetCookie(const GURL& url, + const GURL& policy_url, + const std::string& cookie); + void OnGetCookies(const GURL& url, const GURL& policy_url, + std::string* cookies); + void OnShowModalHTMLDialog(const GURL& url, int width, int height, + const std::string& json_arguments, + std::string* json_retval); + void OnMissingPluginStatus(int status); + void OnGetCPBrowsingContext(uint32* context); + + // Draw a graphic indicating a crashed plugin. + void PaintSadPlugin(HDC hdc, const gfx::Rect& rect); + + RenderView* render_view_; + WebPlugin* plugin_; + bool windowless_; + bool first_paint_; + scoped_refptr<PluginChannelHost> channel_host_; + std::string mime_type_; + std::string clsid_; + int instance_id_; + std::wstring plugin_path_; + + gfx::Rect plugin_rect_; + gfx::Rect deferred_clip_rect_; + bool send_deferred_update_geometry_; + bool visible_; + + NPObject* npobject_; + NPObjectStub* window_script_object_; + + // Event passed in by the plugin process and is used to decide if + // messages need to be pumped in the NPP_HandleEvent sync call. + HANDLE modal_loop_pump_messages_event_; + + // Bitmap for crashed plugin + SkBitmap* sad_plugin_; + + DISALLOW_EVIL_CONSTRUCTORS(WebPluginDelegateProxy); +}; + +#endif // #ifndef CHROME_RENDERER_WEBPLUGIN_DELEGATE_PROXY_H__ |