summaryrefslogtreecommitdiffstats
path: root/webkit/glue/plugins
diff options
context:
space:
mode:
authorchase@chromium.org <chase@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-21 00:23:26 +0000
committerchase@chromium.org <chase@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-21 00:23:26 +0000
commitc85b0ba71b5a55647b01de9d345e46896979033d (patch)
treeae99657ca1440b7540da473e1bbe6156e43e9be3 /webkit/glue/plugins
parent887ba3adc7b32b7df315ef292ae9395fd75653e5 (diff)
downloadchromium_src-c85b0ba71b5a55647b01de9d345e46896979033d.zip
chromium_src-c85b0ba71b5a55647b01de9d345e46896979033d.tar.gz
chromium_src-c85b0ba71b5a55647b01de9d345e46896979033d.tar.bz2
Revert "Move the NPAPI files from webkit/glue/plugins to webkit/plugins/npapi"
Manually reverting r69755, which broke the tree. BUG=none TEST=none TBR=dmaclach@chromium.org Review URL: http://codereview.chromium.org/5998002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@69771 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/glue/plugins')
-rw-r--r--webkit/glue/plugins/DEPS9
-rw-r--r--webkit/glue/plugins/carbon_plugin_window_tracker_mac.cc55
-rw-r--r--webkit/glue/plugins/carbon_plugin_window_tracker_mac.h53
-rw-r--r--webkit/glue/plugins/coregraphics_private_symbols_mac.h27
-rw-r--r--webkit/glue/plugins/default_plugin_shared.h31
-rw-r--r--webkit/glue/plugins/gtk_plugin_container.cc85
-rw-r--r--webkit/glue/plugins/gtk_plugin_container.h26
-rw-r--r--webkit/glue/plugins/gtk_plugin_container_manager.cc155
-rw-r--r--webkit/glue/plugins/gtk_plugin_container_manager.h57
-rw-r--r--webkit/glue/plugins/npapi_extension_thunk.cc551
-rw-r--r--webkit/glue/plugins/npapi_extension_thunk.h23
-rw-r--r--webkit/glue/plugins/plugin_constants_win.h41
-rw-r--r--webkit/glue/plugins/plugin_group.cc407
-rw-r--r--webkit/glue/plugins/plugin_group.h205
-rw-r--r--webkit/glue/plugins/plugin_group_unittest.cc224
-rw-r--r--webkit/glue/plugins/plugin_host.cc1111
-rw-r--r--webkit/glue/plugins/plugin_host.h63
-rw-r--r--webkit/glue/plugins/plugin_instance.cc680
-rw-r--r--webkit/glue/plugins/plugin_instance.h375
-rw-r--r--webkit/glue/plugins/plugin_instance_mac.mm133
-rw-r--r--webkit/glue/plugins/plugin_lib.cc349
-rw-r--r--webkit/glue/plugins/plugin_lib.h120
-rw-r--r--webkit/glue/plugins/plugin_lib_mac.mm348
-rw-r--r--webkit/glue/plugins/plugin_lib_posix.cc256
-rw-r--r--webkit/glue/plugins/plugin_lib_unittest.cc152
-rw-r--r--webkit/glue/plugins/plugin_lib_win.cc46
-rw-r--r--webkit/glue/plugins/plugin_list.cc781
-rw-r--r--webkit/glue/plugins/plugin_list.h332
-rw-r--r--webkit/glue/plugins/plugin_list_mac.mm109
-rw-r--r--webkit/glue/plugins/plugin_list_posix.cc270
-rw-r--r--webkit/glue/plugins/plugin_list_win.cc410
-rw-r--r--webkit/glue/plugins/plugin_stream.cc258
-rw-r--r--webkit/glue/plugins/plugin_stream.h156
-rw-r--r--webkit/glue/plugins/plugin_stream_posix.cc74
-rw-r--r--webkit/glue/plugins/plugin_stream_url.cc130
-rw-r--r--webkit/glue/plugins/plugin_stream_url.h65
-rw-r--r--webkit/glue/plugins/plugin_stream_win.cc97
-rw-r--r--webkit/glue/plugins/plugin_string_stream.cc37
-rw-r--r--webkit/glue/plugins/plugin_string_stream.h39
-rw-r--r--webkit/glue/plugins/plugin_stubs.cc30
-rw-r--r--webkit/glue/plugins/plugin_web_event_converter_mac.h60
-rw-r--r--webkit/glue/plugins/plugin_web_event_converter_mac.mm359
-rw-r--r--webkit/glue/plugins/quickdraw_drawing_manager_mac.cc154
-rw-r--r--webkit/glue/plugins/quickdraw_drawing_manager_mac.h83
-rw-r--r--webkit/glue/plugins/test/Info.plist46
-rw-r--r--webkit/glue/plugins/test/npapi_constants.cc10
-rw-r--r--webkit/glue/plugins/test/npapi_constants.h19
-rw-r--r--webkit/glue/plugins/test/npapi_test.cc122
-rw-r--r--webkit/glue/plugins/test/npapi_test.def6
-rw-r--r--webkit/glue/plugins/test/npapi_test.rc102
-rw-r--r--webkit/glue/plugins/test/plugin_arguments_test.cc69
-rw-r--r--webkit/glue/plugins/test/plugin_arguments_test.h43
-rw-r--r--webkit/glue/plugins/test/plugin_client.cc240
-rw-r--r--webkit/glue/plugins/test/plugin_client.h45
-rw-r--r--webkit/glue/plugins/test/plugin_create_instance_in_paint.cc78
-rw-r--r--webkit/glue/plugins/test/plugin_create_instance_in_paint.h33
-rw-r--r--webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.cc45
-rw-r--r--webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h30
-rw-r--r--webkit/glue/plugins/test/plugin_get_javascript_url2_test.cc134
-rw-r--r--webkit/glue/plugins/test/plugin_get_javascript_url2_test.h38
-rw-r--r--webkit/glue/plugins/test/plugin_get_javascript_url_test.cc218
-rw-r--r--webkit/glue/plugins/test/plugin_get_javascript_url_test.h47
-rw-r--r--webkit/glue/plugins/test/plugin_geturl_test.cc414
-rw-r--r--webkit/glue/plugins/test/plugin_geturl_test.h61
-rw-r--r--webkit/glue/plugins/test/plugin_javascript_open_popup.cc103
-rw-r--r--webkit/glue/plugins/test/plugin_javascript_open_popup.h47
-rw-r--r--webkit/glue/plugins/test/plugin_new_fails_test.cc18
-rw-r--r--webkit/glue/plugins/test/plugin_new_fails_test.h21
-rw-r--r--webkit/glue/plugins/test/plugin_npobject_lifetime_test.cc174
-rw-r--r--webkit/glue/plugins/test/plugin_npobject_lifetime_test.h82
-rw-r--r--webkit/glue/plugins/test/plugin_npobject_proxy_test.cc51
-rw-r--r--webkit/glue/plugins/test/plugin_npobject_proxy_test.h27
-rw-r--r--webkit/glue/plugins/test/plugin_private_test.cc57
-rw-r--r--webkit/glue/plugins/test/plugin_private_test.h25
-rw-r--r--webkit/glue/plugins/test/plugin_schedule_timer_test.cc116
-rw-r--r--webkit/glue/plugins/test/plugin_schedule_timer_test.h68
-rw-r--r--webkit/glue/plugins/test/plugin_setup_test.cc22
-rw-r--r--webkit/glue/plugins/test/plugin_setup_test.h24
-rw-r--r--webkit/glue/plugins/test/plugin_test.cc155
-rw-r--r--webkit/glue/plugins/test/plugin_test.h134
-rw-r--r--webkit/glue/plugins/test/plugin_test_factory.cc104
-rw-r--r--webkit/glue/plugins/test/plugin_test_factory.h22
-rw-r--r--webkit/glue/plugins/test/plugin_thread_async_call_test.cc117
-rw-r--r--webkit/glue/plugins/test/plugin_thread_async_call_test.h39
-rw-r--r--webkit/glue/plugins/test/plugin_window_size_test.cc55
-rw-r--r--webkit/glue/plugins/test/plugin_window_size_test.h24
-rw-r--r--webkit/glue/plugins/test/plugin_windowed_test.cc150
-rw-r--r--webkit/glue/plugins/test/plugin_windowed_test.h33
-rw-r--r--webkit/glue/plugins/test/plugin_windowless_test.cc261
-rw-r--r--webkit/glue/plugins/test/plugin_windowless_test.h35
-rw-r--r--webkit/glue/plugins/test/resource.h15
-rw-r--r--webkit/glue/plugins/url_request_info_unittest.cc249
-rw-r--r--webkit/glue/plugins/webplugin.cc31
-rw-r--r--webkit/glue/plugins/webplugin.h200
-rw-r--r--webkit/glue/plugins/webplugin_2d_device_delegate.cc53
-rw-r--r--webkit/glue/plugins/webplugin_2d_device_delegate.h43
-rw-r--r--webkit/glue/plugins/webplugin_3d_device_delegate.cc110
-rw-r--r--webkit/glue/plugins/webplugin_3d_device_delegate.h71
-rw-r--r--webkit/glue/plugins/webplugin_accelerated_surface_mac.h44
-rw-r--r--webkit/glue/plugins/webplugin_audio_device_delegate.cc50
-rw-r--r--webkit/glue/plugins/webplugin_audio_device_delegate.h42
-rw-r--r--webkit/glue/plugins/webplugin_delegate.cc40
-rw-r--r--webkit/glue/plugins/webplugin_delegate.h166
-rw-r--r--webkit/glue/plugins/webplugin_delegate_impl.cc304
-rw-r--r--webkit/glue/plugins/webplugin_delegate_impl.h511
-rw-r--r--webkit/glue/plugins/webplugin_delegate_impl_gtk.cc767
-rw-r--r--webkit/glue/plugins/webplugin_delegate_impl_mac.mm1145
-rw-r--r--webkit/glue/plugins/webplugin_delegate_impl_win.cc1410
-rw-r--r--webkit/glue/plugins/webplugin_file_delegate.cc17
-rw-r--r--webkit/glue/plugins/webplugin_file_delegate.h33
-rw-r--r--webkit/glue/plugins/webplugin_impl.cc1393
-rw-r--r--webkit/glue/plugins/webplugin_impl.h330
-rw-r--r--webkit/glue/plugins/webplugin_impl_unittest.cc232
-rw-r--r--webkit/glue/plugins/webplugin_page_delegate.h69
-rw-r--r--webkit/glue/plugins/webplugin_print_delegate.cc26
-rw-r--r--webkit/glue/plugins/webplugin_print_delegate.h43
-rw-r--r--webkit/glue/plugins/webplugininfo.cc44
-rw-r--r--webkit/glue/plugins/webplugininfo.h60
-rw-r--r--webkit/glue/plugins/webview_plugin.cc235
-rw-r--r--webkit/glue/plugins/webview_plugin.h142
120 files changed, 20652 insertions, 13 deletions
diff --git a/webkit/glue/plugins/DEPS b/webkit/glue/plugins/DEPS
new file mode 100644
index 0000000..024a4ef
--- /dev/null
+++ b/webkit/glue/plugins/DEPS
@@ -0,0 +1,9 @@
+include_rules = [
+ "+ppapi",
+
+ # Files in this directory must not depend on the proxy, because the proxy
+ # depends on IPC which we don't want to have in /webkit.
+ "-ppapi/proxy",
+
+ "+printing",
+]
diff --git a/webkit/glue/plugins/carbon_plugin_window_tracker_mac.cc b/webkit/glue/plugins/carbon_plugin_window_tracker_mac.cc
new file mode 100644
index 0000000..c4ae72d
--- /dev/null
+++ b/webkit/glue/plugins/carbon_plugin_window_tracker_mac.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "webkit/glue/plugins/carbon_plugin_window_tracker_mac.h"
+
+CarbonPluginWindowTracker::CarbonPluginWindowTracker() {
+}
+
+CarbonPluginWindowTracker* CarbonPluginWindowTracker::SharedInstance() {
+ static CarbonPluginWindowTracker* tracker = new CarbonPluginWindowTracker();
+ return tracker;
+}
+
+WindowRef CarbonPluginWindowTracker::CreateDummyWindowForDelegate(
+ OpaquePluginRef delegate) {
+ // The real size will be set by the plugin instance, once that size is known.
+ Rect window_bounds = { 0, 0, 100, 100 };
+ WindowRef new_ref = NULL;
+ if (CreateNewWindow(kDocumentWindowClass,
+ kWindowNoTitleBarAttribute,
+ &window_bounds,
+ &new_ref) == noErr) {
+ window_to_delegate_map_[new_ref] = delegate;
+ delegate_to_window_map_[delegate] = new_ref;
+ }
+ return new_ref;
+}
+
+OpaquePluginRef CarbonPluginWindowTracker::GetDelegateForDummyWindow(
+ WindowRef window) const {
+ WindowToDelegateMap::const_iterator i = window_to_delegate_map_.find(window);
+ if (i != window_to_delegate_map_.end())
+ return i->second;
+ return NULL;
+}
+
+WindowRef CarbonPluginWindowTracker::GetDummyWindowForDelegate(
+ OpaquePluginRef delegate) const {
+ DelegateToWindowMap::const_iterator i =
+ delegate_to_window_map_.find(delegate);
+ if (i != delegate_to_window_map_.end())
+ return i->second;
+ return NULL;
+}
+
+void CarbonPluginWindowTracker::DestroyDummyWindowForDelegate(
+ OpaquePluginRef delegate, WindowRef window) {
+ DCHECK(GetDelegateForDummyWindow(window) == delegate);
+ window_to_delegate_map_.erase(window);
+ delegate_to_window_map_.erase(delegate);
+ if (window) // Check just in case the initial window creation failed.
+ DisposeWindow(window);
+}
diff --git a/webkit/glue/plugins/carbon_plugin_window_tracker_mac.h b/webkit/glue/plugins/carbon_plugin_window_tracker_mac.h
new file mode 100644
index 0000000..90fc318
--- /dev/null
+++ b/webkit/glue/plugins/carbon_plugin_window_tracker_mac.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_CARBON_PLUGIN_WINDOW_TRACKER_MAC_H_
+#define WEBKIT_GLUE_PLUGINS_CARBON_PLUGIN_WINDOW_TRACKER_MAC_H_
+
+#include <Carbon/Carbon.h>
+#include <map>
+
+#include "base/basictypes.h"
+
+// This is really a WebPluginDelegateImpl, but that class is private to the
+// framework, and these functions are called from a dylib.
+typedef void* OpaquePluginRef;
+
+// Creates and tracks the invisible windows that are necessary for
+// Carbon-event-model plugins.
+//
+// Serves as a bridge between plugin delegate instances and the Carbon
+// interposing library. The Carbon functions we interpose work in terms of
+// WindowRefs, and we need to be able to map from those back to the plugin
+// delegates that know what we should claim about the state of the window.
+class __attribute__((visibility("default"))) CarbonPluginWindowTracker {
+ public:
+ CarbonPluginWindowTracker();
+
+ // Returns the shared window tracker instance.
+ static CarbonPluginWindowTracker* SharedInstance();
+
+ // Creates a new carbon window associated with |delegate|.
+ WindowRef CreateDummyWindowForDelegate(OpaquePluginRef delegate);
+
+ // Returns the WebPluginDelegate associated with the given dummy window.
+ OpaquePluginRef GetDelegateForDummyWindow(WindowRef window) const;
+
+ // Returns the dummy window associated with |delegate|.
+ WindowRef GetDummyWindowForDelegate(OpaquePluginRef delegate) const;
+
+ // Destroys the dummy window for |delegate|.
+ void DestroyDummyWindowForDelegate(OpaquePluginRef delegate,
+ WindowRef window);
+
+ private:
+ typedef std::map<WindowRef, OpaquePluginRef> WindowToDelegateMap;
+ typedef std::map<OpaquePluginRef, WindowRef> DelegateToWindowMap;
+ WindowToDelegateMap window_to_delegate_map_;
+ DelegateToWindowMap delegate_to_window_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(CarbonPluginWindowTracker);
+};
+
+#endif // WEBKIT_GLUE_PLUGINS_CARBON_PLUGIN_WINDOW_TRACKER_MAC_H_
diff --git a/webkit/glue/plugins/coregraphics_private_symbols_mac.h b/webkit/glue/plugins/coregraphics_private_symbols_mac.h
new file mode 100644
index 0000000..0342d6f
--- /dev/null
+++ b/webkit/glue/plugins/coregraphics_private_symbols_mac.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_COREGRAPHICS_PRIVATE_SYMBOLS_MAC_H_
+#define WEBKIT_GLUE_PLUGINS_COREGRAPHICS_PRIVATE_SYMBOLS_MAC_H_
+
+// These are CoreGraphics SPI, verified to exist in both 10.5 and 10.6.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Copies the contents of the window with id |wid| into the given rect in the
+// given context
+OSStatus CGContextCopyWindowCaptureContentsToRect(
+ CGContextRef, CGRect, int cid, int wid, int unknown);
+
+// Returns the connection ID we need for the third argument to
+// CGContextCopyWindowCaptureContentsToRect
+int _CGSDefaultConnection(void);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // WEBKIT_GLUE_PLUGINS_COREGRAPHICS_PRIVATE_SYMBOLS_MAC_H_
diff --git a/webkit/glue/plugins/default_plugin_shared.h b/webkit/glue/plugins/default_plugin_shared.h
new file mode 100644
index 0000000..79d06b3
--- /dev/null
+++ b/webkit/glue/plugins/default_plugin_shared.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Thes file contains stuff that should be shared among projects that do some
+// special handling with default plugin
+
+#ifndef WEBKIT_GLUE_PLUGINS_DEFAULT_PLUGIN_SHARED_H
+#define WEBKIT_GLUE_PLUGINS_DEFAULT_PLUGIN_SHARED_H
+
+namespace default_plugin {
+
+// We use the NPNGetValue host function to send notification message to host.
+// This corresponds to NPNVariable defined in npapi.h, and should be chosen so
+// as to not overlap values if NPAPI is updated.
+
+const int kMissingPluginStatusStart = 5000;
+
+enum MissingPluginStatus {
+ MISSING_PLUGIN_AVAILABLE,
+ MISSING_PLUGIN_USER_STARTED_DOWNLOAD
+};
+
+#if defined(OS_WIN)
+#include <windows.h>
+const int kInstallMissingPluginMessage = WM_APP + 117;
+#endif
+
+} // namespace default_plugin
+
+#endif // WEBKIT_GLUE_PLUGINS_DEFAULT_PLUGIN_SHARED_H
diff --git a/webkit/glue/plugins/gtk_plugin_container.cc b/webkit/glue/plugins/gtk_plugin_container.cc
new file mode 100644
index 0000000..c80bbf1
--- /dev/null
+++ b/webkit/glue/plugins/gtk_plugin_container.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/gtk_plugin_container.h"
+
+#include <gtk/gtk.h>
+
+#include "base/basictypes.h"
+
+namespace {
+
+// NOTE: This class doesn't have constructors/destructors, it is created
+// through GLib's object management.
+class GtkPluginContainer : public GtkSocket {
+ public:
+ // Sets the requested size of the widget.
+ void set_size(int width, int height) {
+ width_ = width;
+ height_ = height;
+ }
+
+ // Casts a widget into a GtkPluginContainer, after checking the type.
+ template <class T>
+ static GtkPluginContainer *CastChecked(T *instance) {
+ return G_TYPE_CHECK_INSTANCE_CAST(instance, GetType(), GtkPluginContainer);
+ }
+
+ // Create and register our custom container type with GTK.
+ static GType GetType() {
+ static GType type = 0; // We only want to register our type once.
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof(GtkSocketClass),
+ NULL, NULL,
+ static_cast<GClassInitFunc>(&ClassInit),
+ NULL, NULL,
+ sizeof(GtkPluginContainer),
+ 0, &InstanceInit,
+ };
+ type = g_type_register_static(GTK_TYPE_SOCKET,
+ "GtkPluginContainer",
+ &info,
+ static_cast<GTypeFlags>(0));
+ }
+ return type;
+ }
+
+ // Implementation of the class initializer.
+ static void ClassInit(gpointer klass, gpointer class_data_unusued) {
+ GtkWidgetClass* widget_class = reinterpret_cast<GtkWidgetClass*>(klass);
+ widget_class->size_request = &HandleSizeRequest;
+ }
+
+ // Implementation of the instance initializer (constructor).
+ static void InstanceInit(GTypeInstance *instance, gpointer klass) {
+ GtkPluginContainer *container = CastChecked(instance);
+ container->set_size(0, 0);
+ }
+
+ // Report our allocation size during size requisition.
+ static void HandleSizeRequest(GtkWidget* widget,
+ GtkRequisition* requisition) {
+ GtkPluginContainer *container = CastChecked(widget);
+ requisition->width = container->width_;
+ requisition->height = container->height_;
+ }
+
+ int width_;
+ int height_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(GtkPluginContainer);
+};
+
+} // anonymous namespace
+
+// Create a new instance of our GTK widget object.
+GtkWidget* gtk_plugin_container_new() {
+ return GTK_WIDGET(g_object_new(GtkPluginContainer::GetType(), NULL));
+}
+
+void gtk_plugin_container_set_size(GtkWidget *widget, int width, int height) {
+ GtkPluginContainer::CastChecked(widget)->set_size(width, height);
+ // Signal the parent that the size request has changed.
+ gtk_widget_queue_resize_no_redraw(widget);
+}
diff --git a/webkit/glue/plugins/gtk_plugin_container.h b/webkit/glue/plugins/gtk_plugin_container.h
new file mode 100644
index 0000000..eed6b94
--- /dev/null
+++ b/webkit/glue/plugins/gtk_plugin_container.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_GTK_PLUGIN_CONTAINER_H_
+#define WEBKIT_GLUE_PLUGINS_GTK_PLUGIN_CONTAINER_H_
+
+// Windowed plugins are embedded via XEmbed, which is implemented by
+// GtkPlug/GtkSocket. But we want to control sizing and positioning
+// directly, so we need a subclass of GtkSocket that sidesteps the
+// size_request handler.
+//
+// The custom size_request handler just reports the size set by
+// gtk_plugin_container_set_size.
+
+typedef struct _GtkWidget GtkWidget;
+
+// Return a new GtkPluginContainer.
+// Intentionally GTK-style here since we're creating a custom GTK widget.
+// This is a GtkSocket subclass; see its documentation for available methods.
+GtkWidget* gtk_plugin_container_new();
+
+// Sets the size of the GtkPluginContainer.
+void gtk_plugin_container_set_size(GtkWidget *widget, int width, int height);
+
+#endif // WEBKIT_GLUE_PLUGINS_GTK_PLUGIN_CONTAINER_H_
diff --git a/webkit/glue/plugins/gtk_plugin_container_manager.cc b/webkit/glue/plugins/gtk_plugin_container_manager.cc
new file mode 100644
index 0000000..2f82b24
--- /dev/null
+++ b/webkit/glue/plugins/gtk_plugin_container_manager.cc
@@ -0,0 +1,155 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/gtk_plugin_container_manager.h"
+
+#include <gtk/gtk.h>
+
+#include "base/logging.h"
+#include "gfx/gtk_util.h"
+#include "webkit/glue/plugins/gtk_plugin_container.h"
+#include "webkit/glue/plugins/webplugin.h"
+
+GtkPluginContainerManager::GtkPluginContainerManager() : host_widget_(NULL) {}
+
+GtkPluginContainerManager::~GtkPluginContainerManager() {}
+
+GtkWidget* GtkPluginContainerManager::CreatePluginContainer(
+ gfx::PluginWindowHandle id) {
+ DCHECK(host_widget_);
+ GtkWidget *widget = gtk_plugin_container_new();
+ plugin_window_to_widget_map_.insert(std::make_pair(id, widget));
+
+ // The Realize callback is responsible for adding the plug into the socket.
+ // The reason is 2-fold:
+ // - the plug can't be added until the socket is realized, but this may not
+ // happen until the socket is attached to a top-level window, which isn't the
+ // case for background tabs.
+ // - when dragging tabs, the socket gets unrealized, which breaks the XEMBED
+ // connection. We need to make it again when the tab is reattached, and the
+ // socket gets realized again.
+ //
+ // Note, the RealizeCallback relies on the plugin_window_to_widget_map_ to
+ // have the mapping.
+ g_signal_connect(widget, "realize",
+ G_CALLBACK(RealizeCallback), this);
+
+ // Don't destroy the widget when the plug is removed.
+ g_signal_connect(widget, "plug-removed",
+ G_CALLBACK(gtk_true), NULL);
+
+ gtk_container_add(GTK_CONTAINER(host_widget_), widget);
+ gtk_widget_show(widget);
+
+ return widget;
+}
+
+void GtkPluginContainerManager::DestroyPluginContainer(
+ gfx::PluginWindowHandle id) {
+ DCHECK(host_widget_);
+ GtkWidget* widget = MapIDToWidget(id);
+ if (widget)
+ gtk_widget_destroy(widget);
+
+ plugin_window_to_widget_map_.erase(id);
+}
+
+void GtkPluginContainerManager::MovePluginContainer(
+ const webkit_glue::WebPluginGeometry& move) {
+ DCHECK(host_widget_);
+ GtkWidget *widget = MapIDToWidget(move.window);
+ if (!widget)
+ return;
+
+ DCHECK(!GTK_WIDGET_NO_WINDOW(widget));
+
+ if (!move.visible) {
+ gtk_widget_hide(widget);
+ return;
+ }
+
+ gtk_widget_show(widget);
+
+ if (!move.rects_valid)
+ return;
+
+ // TODO(piman): if the widget hasn't been realized (e.g. the tab has been
+ // torn off and the parent gtk widget has been detached from the hierarchy),
+ // we lose the cutout information.
+ if (GTK_WIDGET_REALIZED(widget)) {
+ GdkRectangle clip_rect = move.clip_rect.ToGdkRectangle();
+ GdkRegion* clip_region = gdk_region_rectangle(&clip_rect);
+ gfx::SubtractRectanglesFromRegion(clip_region, move.cutout_rects);
+ gdk_window_shape_combine_region(widget->window, clip_region, 0, 0);
+ gdk_region_destroy(clip_region);
+ }
+
+ // Update the window position. Resizing is handled by WebPluginDelegate.
+ // TODO(deanm): Verify that we only need to move and not resize.
+ // TODO(evanm): we should cache the last shape and position and skip all
+ // of this business in the common case where nothing has changed.
+ int current_x, current_y;
+
+ // Until the above TODO is resolved, we can grab the last position
+ // off of the GtkFixed with a bit of hackery.
+ GValue value = {0};
+ g_value_init(&value, G_TYPE_INT);
+ gtk_container_child_get_property(GTK_CONTAINER(host_widget_), widget,
+ "x", &value);
+ current_x = g_value_get_int(&value);
+ gtk_container_child_get_property(GTK_CONTAINER(host_widget_), widget,
+ "y", &value);
+ current_y = g_value_get_int(&value);
+ g_value_unset(&value);
+
+ if (move.window_rect.x() != current_x ||
+ move.window_rect.y() != current_y) {
+ // Calling gtk_fixed_move unnecessarily is a no-no, as it causes the
+ // parent window to repaint!
+ gtk_fixed_move(GTK_FIXED(host_widget_),
+ widget,
+ move.window_rect.x(),
+ move.window_rect.y());
+ }
+
+ gtk_plugin_container_set_size(widget,
+ move.window_rect.width(),
+ move.window_rect.height());
+}
+
+GtkWidget* GtkPluginContainerManager::MapIDToWidget(
+ gfx::PluginWindowHandle id) {
+ PluginWindowToWidgetMap::const_iterator i =
+ plugin_window_to_widget_map_.find(id);
+ if (i != plugin_window_to_widget_map_.end())
+ return i->second;
+
+ LOG(ERROR) << "Request for widget host for unknown window id " << id;
+
+ return NULL;
+}
+
+gfx::PluginWindowHandle GtkPluginContainerManager::MapWidgetToID(
+ GtkWidget* widget) {
+ for (PluginWindowToWidgetMap::const_iterator i =
+ plugin_window_to_widget_map_.begin();
+ i != plugin_window_to_widget_map_.end(); ++i) {
+ if (i->second == widget)
+ return i->first;
+ }
+
+ LOG(ERROR) << "Request for id for unknown widget";
+ return 0;
+}
+
+// static
+void GtkPluginContainerManager::RealizeCallback(GtkWidget* widget,
+ void* user_data) {
+ GtkPluginContainerManager* plugin_container_manager =
+ static_cast<GtkPluginContainerManager*>(user_data);
+
+ gfx::PluginWindowHandle id = plugin_container_manager->MapWidgetToID(widget);
+ if (id)
+ gtk_socket_add_id(GTK_SOCKET(widget), id);
+}
diff --git a/webkit/glue/plugins/gtk_plugin_container_manager.h b/webkit/glue/plugins/gtk_plugin_container_manager.h
new file mode 100644
index 0000000..7f7db8d
--- /dev/null
+++ b/webkit/glue/plugins/gtk_plugin_container_manager.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_GTK_PLUGIN_CONTAINER_MANAGER_H_
+#define WEBKIT_GLUE_PLUGINS_GTK_PLUGIN_CONTAINER_MANAGER_H_
+
+#include <gtk/gtk.h>
+#include <map>
+
+#include "gfx/native_widget_types.h"
+
+typedef struct _GtkWidget GtkWidget;
+
+namespace webkit_glue {
+struct WebPluginGeometry;
+}
+
+// Helper class that creates and manages plugin containers (GtkSocket).
+class GtkPluginContainerManager {
+ public:
+ GtkPluginContainerManager();
+ ~GtkPluginContainerManager();
+
+ // Sets the widget that will host the plugin containers. Must be a GtkFixed.
+ void set_host_widget(GtkWidget *widget) { host_widget_ = widget; }
+
+ // Creates a new plugin container, for a given plugin XID.
+ GtkWidget* CreatePluginContainer(gfx::PluginWindowHandle id);
+
+ // Destroys a plugin container, given the plugin XID.
+ void DestroyPluginContainer(gfx::PluginWindowHandle id);
+
+ // Takes an update from WebKit about a plugin's position and side and moves
+ // the plugin accordingly.
+ void MovePluginContainer(const webkit_glue::WebPluginGeometry& move);
+
+ private:
+ // Maps a plugin XID to the corresponding container widget.
+ GtkWidget* MapIDToWidget(gfx::PluginWindowHandle id);
+
+ // Maps a container widget to the corresponding plugin XID.
+ gfx::PluginWindowHandle MapWidgetToID(GtkWidget* widget);
+
+ // Callback for when the plugin container gets realized, at which point it
+ // plugs the plugin XID.
+ static void RealizeCallback(GtkWidget *widget, void *user_data);
+
+ // Parent of the plugin containers.
+ GtkWidget* host_widget_;
+
+ // A map that associates plugin containers to the plugin XID.
+ typedef std::map<gfx::PluginWindowHandle, GtkWidget*> PluginWindowToWidgetMap;
+ PluginWindowToWidgetMap plugin_window_to_widget_map_;
+};
+
+#endif // WEBKIT_GLUE_PLUGINS_GTK_PLUGIN_CONTAINER_MANAGER_H_
diff --git a/webkit/glue/plugins/npapi_extension_thunk.cc b/webkit/glue/plugins/npapi_extension_thunk.cc
new file mode 100644
index 0000000..05a9c5d
--- /dev/null
+++ b/webkit/glue/plugins/npapi_extension_thunk.cc
@@ -0,0 +1,551 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/npapi_extension_thunk.h"
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/webplugin.h"
+#include "webkit/glue/plugins/webplugin_delegate.h"
+#include "webkit/glue/webkit_glue.h"
+
+// FindInstance()
+// Finds a PluginInstance from an NPP.
+// The caller must take a reference if needed.
+static NPAPI::PluginInstance* FindInstance(NPP id) {
+ if (id == NULL) {
+ NOTREACHED();
+ return NULL;
+ }
+ return static_cast<NPAPI::PluginInstance*>(id->ndata);
+}
+
+// 2D device API ---------------------------------------------------------------
+
+static NPError Device2DQueryCapability(NPP id, int32_t capability,
+ int32_t* value) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ plugin->webplugin()->delegate()->Device2DQueryCapability(capability, value);
+ return NPERR_NO_ERROR;
+ } else {
+ return NPERR_GENERIC_ERROR;
+ }
+}
+
+static NPError Device2DQueryConfig(NPP id,
+ const NPDeviceConfig* request,
+ NPDeviceConfig* obtain) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device2DQueryConfig(
+ static_cast<const NPDeviceContext2DConfig*>(request),
+ static_cast<NPDeviceContext2DConfig*>(obtain));
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device2DInitializeContext(NPP id,
+ const NPDeviceConfig* config,
+ NPDeviceContext* context) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device2DInitializeContext(
+ static_cast<const NPDeviceContext2DConfig*>(config),
+ static_cast<NPDeviceContext2D*>(context));
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device2DSetStateContext(NPP id,
+ NPDeviceContext* context,
+ int32_t state,
+ intptr_t value) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device2DSetStateContext(
+ static_cast<NPDeviceContext2D*>(context), state, value);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device2DGetStateContext(NPP id,
+ NPDeviceContext* context,
+ int32_t state,
+ intptr_t* value) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device2DGetStateContext(
+ static_cast<NPDeviceContext2D*>(context), state, value);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device2DFlushContext(NPP id,
+ NPDeviceContext* context,
+ NPDeviceFlushContextCallbackPtr callback,
+ void* user_data) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ NPError err = plugin->webplugin()->delegate()->Device2DFlushContext(
+ id, static_cast<NPDeviceContext2D*>(context), callback, user_data);
+
+ // Invoke the callback to inform the caller the work was done.
+ // TODO(brettw) this is probably not how we want this to work, this should
+ // happen when the frame is painted so the plugin knows when it can draw
+ // the next frame.
+ if (callback != NULL)
+ (*callback)(id, context, err, user_data);
+
+ // Return any errors.
+ return err;
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device2DDestroyContext(NPP id,
+ NPDeviceContext* context) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device2DDestroyContext(
+ static_cast<NPDeviceContext2D*>(context));
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device2DCreateBuffer(NPP id,
+ NPDeviceContext* context,
+ size_t size,
+ int32_t* buffer_id) {
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device2DDestroyBuffer(NPP id,
+ NPDeviceContext* context,
+ int32_t buffer_id) {
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device2DMapBuffer(NPP id,
+ NPDeviceContext* context,
+ int32_t buffer_id,
+ NPDeviceBuffer* buffer) {
+ return NPERR_GENERIC_ERROR;
+}
+
+// 3D device API ---------------------------------------------------------------
+
+static NPError Device3DQueryCapability(NPP id, int32_t capability,
+ int32_t* value) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ plugin->webplugin()->delegate()->Device3DQueryCapability(capability, value);
+ return NPERR_NO_ERROR;
+ } else {
+ return NPERR_GENERIC_ERROR;
+ }
+}
+
+static NPError Device3DQueryConfig(NPP id,
+ const NPDeviceConfig* request,
+ NPDeviceConfig* obtain) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DQueryConfig(
+ static_cast<const NPDeviceContext3DConfig*>(request),
+ static_cast<NPDeviceContext3DConfig*>(obtain));
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DInitializeContext(NPP id,
+ const NPDeviceConfig* config,
+ NPDeviceContext* context) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DInitializeContext(
+ static_cast<const NPDeviceContext3DConfig*>(config),
+ static_cast<NPDeviceContext3D*>(context));
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DSetStateContext(NPP id,
+ NPDeviceContext* context,
+ int32_t state,
+ intptr_t value) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DSetStateContext(
+ static_cast<NPDeviceContext3D*>(context), state, value);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DGetStateContext(NPP id,
+ NPDeviceContext* context,
+ int32_t state,
+ intptr_t* value) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DGetStateContext(
+ static_cast<NPDeviceContext3D*>(context), state, value);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DFlushContext(NPP id,
+ NPDeviceContext* context,
+ NPDeviceFlushContextCallbackPtr callback,
+ void* user_data) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DFlushContext(
+ id, static_cast<NPDeviceContext3D*>(context), callback, user_data);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DDestroyContext(NPP id,
+ NPDeviceContext* context) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DDestroyContext(
+ static_cast<NPDeviceContext3D*>(context));
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DCreateBuffer(NPP id,
+ NPDeviceContext* context,
+ size_t size,
+ int32_t* buffer_id) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DCreateBuffer(
+ static_cast<NPDeviceContext3D*>(context), size, buffer_id);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DDestroyBuffer(NPP id,
+ NPDeviceContext* context,
+ int32_t buffer_id) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DDestroyBuffer(
+ static_cast<NPDeviceContext3D*>(context), buffer_id);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DMapBuffer(NPP id,
+ NPDeviceContext* context,
+ int32_t buffer_id,
+ NPDeviceBuffer* buffer) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DMapBuffer(
+ static_cast<NPDeviceContext3D*>(context), buffer_id, buffer);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+// Experimental 3D device API --------------------------------------------------
+
+static NPError Device3DGetNumConfigs(NPP id, int32_t* num_configs) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DGetNumConfigs(num_configs);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DGetConfigAttribs(NPP id,
+ int32_t config,
+ int32_t* attrib_list) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DGetConfigAttribs(
+ config,
+ attrib_list);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DCreateContext(NPP id,
+ int32_t config,
+ const int32_t* attrib_list,
+ NPDeviceContext** context) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DCreateContext(
+ config,
+ attrib_list,
+ reinterpret_cast<NPDeviceContext3D**>(context));
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DSynchronizeContext(
+ NPP id,
+ NPDeviceContext* context,
+ NPDeviceSynchronizationMode mode,
+ const int32_t* input_attrib_list,
+ int32_t* output_attrib_list,
+ NPDeviceSynchronizeContextCallbackPtr callback,
+ void* callback_data) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DSynchronizeContext(
+ id,
+ static_cast<NPDeviceContext3D*>(context),
+ mode,
+ input_attrib_list,
+ output_attrib_list,
+ callback,
+ callback_data);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError Device3DRegisterCallback(
+ NPP id,
+ NPDeviceContext* context,
+ int32_t callback_type,
+ NPDeviceGenericCallbackPtr callback,
+ void* callback_data) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->Device3DRegisterCallback(
+ id,
+ static_cast<NPDeviceContext3D*>(context),
+ callback_type,
+ callback,
+ callback_data);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+// Audio device API ------------------------------------------------------------
+
+static NPError DeviceAudioQueryCapability(NPP id, int32_t capability,
+ int32_t* value) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ plugin->webplugin()->delegate()->DeviceAudioQueryCapability(capability,
+ value);
+ return NPERR_NO_ERROR;
+ } else {
+ return NPERR_GENERIC_ERROR;
+ }
+}
+
+static NPError DeviceAudioQueryConfig(NPP id,
+ const NPDeviceConfig* request,
+ NPDeviceConfig* obtain) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->DeviceAudioQueryConfig(
+ static_cast<const NPDeviceContextAudioConfig*>(request),
+ static_cast<NPDeviceContextAudioConfig*>(obtain));
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError DeviceAudioInitializeContext(NPP id,
+ const NPDeviceConfig* config,
+ NPDeviceContext* context) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->DeviceAudioInitializeContext(
+ static_cast<const NPDeviceContextAudioConfig*>(config),
+ static_cast<NPDeviceContextAudio*>(context));
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError DeviceAudioSetStateContext(NPP id,
+ NPDeviceContext* context,
+ int32_t state,
+ intptr_t value) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ return plugin->webplugin()->delegate()->DeviceAudioSetStateContext(
+ static_cast<NPDeviceContextAudio*>(context), state, value);
+ }
+ return NPERR_GENERIC_ERROR;
+}
+
+static NPError DeviceAudioGetStateContext(NPP id,
+ NPDeviceContext* context,
+ int32_t state,
+ intptr_t* value) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ return plugin->webplugin()->delegate()->DeviceAudioGetStateContext(
+ static_cast<NPDeviceContextAudio*>(context), state, value);
+}
+
+static NPError DeviceAudioFlushContext(NPP id,
+ NPDeviceContext* context,
+ NPDeviceFlushContextCallbackPtr callback,
+ void* user_data) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ return plugin->webplugin()->delegate()->DeviceAudioFlushContext(
+ id, static_cast<NPDeviceContextAudio*>(context), callback, user_data);
+}
+
+static NPError DeviceAudioDestroyContext(NPP id,
+ NPDeviceContext* context) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ return plugin->webplugin()->delegate()->DeviceAudioDestroyContext(
+ static_cast<NPDeviceContextAudio*>(context));
+}
+// -----------------------------------------------------------------------------
+
+static NPDevice* AcquireDevice(NPP id, NPDeviceID device_id) {
+ static NPDevice device_2d = {
+ Device2DQueryCapability,
+ Device2DQueryConfig,
+ Device2DInitializeContext,
+ Device2DSetStateContext,
+ Device2DGetStateContext,
+ Device2DFlushContext,
+ Device2DDestroyContext,
+ Device2DCreateBuffer,
+ Device2DDestroyBuffer,
+ Device2DMapBuffer,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ };
+ static NPDevice device_3d = {
+ Device3DQueryCapability,
+ Device3DQueryConfig,
+ Device3DInitializeContext,
+ Device3DSetStateContext,
+ Device3DGetStateContext,
+ Device3DFlushContext,
+ Device3DDestroyContext,
+ Device3DCreateBuffer,
+ Device3DDestroyBuffer,
+ Device3DMapBuffer,
+ Device3DGetNumConfigs,
+ Device3DGetConfigAttribs,
+ Device3DCreateContext,
+ Device3DRegisterCallback,
+ Device3DSynchronizeContext,
+ };
+ static NPDevice device_audio = {
+ DeviceAudioQueryCapability,
+ DeviceAudioQueryConfig,
+ DeviceAudioInitializeContext,
+ DeviceAudioSetStateContext,
+ DeviceAudioGetStateContext,
+ DeviceAudioFlushContext,
+ DeviceAudioDestroyContext,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ };
+
+ switch (device_id) {
+ case NPPepper2DDevice:
+ return const_cast<NPDevice*>(&device_2d);
+ case NPPepper3DDevice:
+ return const_cast<NPDevice*>(&device_3d);
+ case NPPepperAudioDevice:
+ return const_cast<NPDevice*>(&device_audio);
+ default:
+ return NULL;
+ }
+}
+
+static NPError ChooseFile(NPP id,
+ const char* mime_types,
+ NPChooseFileMode mode,
+ NPChooseFileCallback callback,
+ void* user_data) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (!plugin)
+ return NPERR_GENERIC_ERROR;
+
+ if (!plugin->webplugin()->delegate()->ChooseFile(mime_types,
+ static_cast<int>(mode),
+ callback, user_data))
+ return NPERR_GENERIC_ERROR;
+
+ return NPERR_NO_ERROR;
+}
+
+static void NumberOfFindResultsChanged(NPP id, int total, bool final_result) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin) {
+ plugin->webplugin()->delegate()->NumberOfFindResultsChanged(
+ total, final_result);
+ }
+}
+
+static void SelectedFindResultChanged(NPP id, int index) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin)
+ plugin->webplugin()->delegate()->SelectedFindResultChanged(index);
+}
+
+static NPWidgetExtensions* GetWidgetExtensions(NPP id) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (!plugin)
+ return NULL;
+
+ return plugin->webplugin()->delegate()->GetWidgetExtensions();
+}
+
+static NPError NPSetCursor(NPP id, NPCursorType type) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (!plugin)
+ return NPERR_GENERIC_ERROR;
+
+ return plugin->webplugin()->delegate()->SetCursor(type) ?
+ NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
+}
+
+static NPFontExtensions* GetFontExtensions(NPP id) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (!plugin)
+ return NULL;
+
+ return plugin->webplugin()->delegate()->GetFontExtensions();
+}
+
+namespace NPAPI {
+
+NPError GetPepperExtensionsFunctions(void* value) {
+ static const NPNExtensions kExtensions = {
+ &AcquireDevice,
+ &NumberOfFindResultsChanged,
+ &SelectedFindResultChanged,
+ &ChooseFile,
+ &GetWidgetExtensions,
+ &NPSetCursor,
+ &GetFontExtensions,
+ };
+
+ // Return a pointer to the canonical function table.
+ NPNExtensions* extensions = const_cast<NPNExtensions*>(&kExtensions);
+ NPNExtensions** exts = reinterpret_cast<NPNExtensions**>(value);
+ *exts = extensions;
+ return NPERR_NO_ERROR;
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/npapi_extension_thunk.h b/webkit/glue/plugins/npapi_extension_thunk.h
new file mode 100644
index 0000000..fada6bc
--- /dev/null
+++ b/webkit/glue/plugins/npapi_extension_thunk.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_NPAPI_EXTENSION_THUNK_H_
+#define WEBKIT_GLUE_PLUGINS_NPAPI_EXTENSION_THUNK_H_
+
+#include "third_party/npapi/bindings/npapi_extensions.h"
+
+// This file implements forwarding for the NPAPI "Pepper" extensions through to
+// the WebPluginDelegate associated with the plugin.
+
+namespace NPAPI {
+
+// Implements NPN_GetValue for the case of NPNVPepperExtensions. The function
+// pointers in the returned structure implement all the extensions.
+NPError GetPepperExtensionsFunctions(void* value);
+
+} // namespace NPAPI
+
+#endif // WEBKIT_GLUE_PLUGINS_NPAPI_EXTENSION_THUNK_H_
+
+
diff --git a/webkit/glue/plugins/plugin_constants_win.h b/webkit/glue/plugins/plugin_constants_win.h
new file mode 100644
index 0000000..9913e5d
--- /dev/null
+++ b/webkit/glue/plugins/plugin_constants_win.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGIN_CONSTANTS_WIN_H_
+#define WEBKIT_GLUE_PLUGIN_CONSTANTS_WIN_H_
+
+// Used by the plugins_test when testing the older WMP plugin to force the new
+// plugin to not get loaded.
+#define kUseOldWMPPluginSwitch "use-old-wmp"
+
+// The window class name for a plugin window.
+#define kNativeWindowClassName L"NativeWindowClass"
+
+// The name of the window class name for the wrapper HWND around the actual
+// plugin window that's used when running in multi-process mode. This window
+// is created on the browser UI thread.
+#define kWrapperNativeWindowClassName L"WrapperNativeWindowClass"
+
+// The name of the custom window message that the browser uses to tell the
+// plugin process to paint a window.
+#define kPaintMessageName L"Chrome_CustomPaint"
+
+// The name of the registry key which NPAPI plugins update on installation.
+#define kRegistryMozillaPlugins L"SOFTWARE\\MozillaPlugins"
+
+#define kMozillaActiveXPlugin L"npmozax.dll"
+#define kNewWMPPlugin L"np-mswmp.dll"
+#define kOldWMPPlugin L"npdsplay.dll"
+#define kYahooApplicationStatePlugin L"npystate.dll"
+#define kWanWangProtocolHandlerPlugin L"npww.dll"
+#define kFlashPlugin L"npswf32.dll"
+#define kAcrobatReaderPlugin L"nppdf32.dll"
+#define kRealPlayerPlugin L"nppl3260.dll"
+#define kSilverlightPlugin L"npctrl.dll"
+#define kJavaPlugin1 L"npjp2.dll"
+#define kJavaPlugin2 L"npdeploytk.dll"
+
+#define kGPUPluginMimeType "application/vnd.google.chrome.gpu-plugin"
+
+#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_LIST_H_
diff --git a/webkit/glue/plugins/plugin_group.cc b/webkit/glue/plugins/plugin_group.cc
new file mode 100644
index 0000000..548e624
--- /dev/null
+++ b/webkit/glue/plugins/plugin_group.cc
@@ -0,0 +1,407 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_group.h"
+
+#include "base/linked_ptr.h"
+#include "base/string_util.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/glue/plugins/webplugininfo.h"
+
+const char* PluginGroup::kAdobeReaderGroupName = "Adobe Reader";
+
+/*static*/
+std::set<string16>* PluginGroup::policy_disabled_plugin_patterns_;
+
+/*static*/
+void PluginGroup::SetPolicyDisabledPluginPatterns(
+ const std::set<string16>& set) {
+ if (!policy_disabled_plugin_patterns_)
+ policy_disabled_plugin_patterns_ = new std::set<string16>(set);
+ else
+ *policy_disabled_plugin_patterns_ = set;
+}
+
+/*static*/
+bool PluginGroup::IsPluginNameDisabledByPolicy(const string16& plugin_name) {
+ if (!policy_disabled_plugin_patterns_)
+ return false;
+
+ std::set<string16>::const_iterator pattern(
+ policy_disabled_plugin_patterns_->begin());
+ while (pattern != policy_disabled_plugin_patterns_->end()) {
+ if (MatchPattern(plugin_name, *pattern))
+ return true;
+ ++pattern;
+ }
+
+ return false;
+}
+
+/*static*/
+bool PluginGroup::IsPluginPathDisabledByPolicy(const FilePath& plugin_path) {
+ std::vector<WebPluginInfo> plugins;
+ NPAPI::PluginList::Singleton()->GetPlugins(false, &plugins);
+ for (std::vector<WebPluginInfo>::const_iterator it = plugins.begin();
+ it != plugins.end();
+ ++it) {
+ if (FilePath::CompareEqualIgnoreCase(it->path.value(),
+ plugin_path.value()) && IsPluginNameDisabledByPolicy(it->name)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+VersionRange::VersionRange(VersionRangeDefinition definition)
+ : low_str(definition.version_matcher_low),
+ high_str(definition.version_matcher_high),
+ min_str(definition.min_version) {
+ if (!low_str.empty())
+ low.reset(Version::GetVersionFromString(low_str));
+ if (!high_str.empty())
+ high.reset(Version::GetVersionFromString(high_str));
+ if (!min_str.empty())
+ min.reset(Version::GetVersionFromString(min_str));
+}
+
+VersionRange::VersionRange(const VersionRange& other) {
+ InitFrom(other);
+}
+
+VersionRange& VersionRange::operator=(const VersionRange& other) {
+ InitFrom(other);
+ return *this;
+}
+
+VersionRange::~VersionRange() {}
+
+void VersionRange::InitFrom(const VersionRange& other) {
+ low_str = other.low_str;
+ high_str = other.high_str;
+ min_str = other.min_str;
+ low.reset(Version::GetVersionFromString(other.low_str));
+ high.reset(Version::GetVersionFromString(other.high_str));
+ min.reset(Version::GetVersionFromString(other.min_str));
+}
+
+PluginGroup::PluginGroup(const string16& group_name,
+ const string16& name_matcher,
+ const std::string& update_url,
+ const std::string& identifier)
+ : identifier_(identifier),
+ group_name_(group_name),
+ name_matcher_(name_matcher),
+ update_url_(update_url),
+ enabled_(false),
+ version_(Version::GetVersionFromString("0")) {
+}
+
+void PluginGroup::InitFrom(const PluginGroup& other) {
+ identifier_ = other.identifier_;
+ group_name_ = other.group_name_;
+ name_matcher_ = other.name_matcher_;
+ description_ = other.description_;
+ update_url_ = other.update_url_;
+ enabled_ = other.enabled_;
+ for (size_t i = 0; i < other.version_ranges_.size(); ++i)
+ version_ranges_.push_back(other.version_ranges_[i]);
+ DCHECK_EQ(other.web_plugin_infos_.size(), other.web_plugin_positions_.size());
+ for (size_t i = 0; i < other.web_plugin_infos_.size(); ++i)
+ AddPlugin(other.web_plugin_infos_[i], other.web_plugin_positions_[i]);
+ if (!version_.get())
+ version_.reset(Version::GetVersionFromString("0"));
+}
+
+PluginGroup::PluginGroup(const PluginGroup& other) {
+ InitFrom(other);
+}
+
+PluginGroup& PluginGroup::operator=(const PluginGroup& other) {
+ version_ranges_.clear();
+ InitFrom(other);
+ return *this;
+}
+
+/*static*/
+PluginGroup* PluginGroup::FromPluginGroupDefinition(
+ const PluginGroupDefinition& definition) {
+ PluginGroup* group = new PluginGroup(ASCIIToUTF16(definition.name),
+ ASCIIToUTF16(definition.name_matcher),
+ definition.update_url,
+ definition.identifier);
+ for (size_t i = 0; i < definition.num_versions; ++i)
+ group->version_ranges_.push_back(VersionRange(definition.versions[i]));
+ return group;
+}
+
+PluginGroup::~PluginGroup() { }
+
+/*static*/
+std::string PluginGroup::GetIdentifier(const WebPluginInfo& wpi) {
+#if defined(OS_POSIX)
+ return wpi.path.BaseName().value();
+#elif defined(OS_WIN)
+ return base::SysWideToUTF8(wpi.path.BaseName().value());
+#endif
+}
+
+/*static*/
+std::string PluginGroup::GetLongIdentifier(const WebPluginInfo& wpi) {
+#if defined(OS_POSIX)
+ return wpi.path.value();
+#elif defined(OS_WIN)
+ return base::SysWideToUTF8(wpi.path.value());
+#endif
+}
+
+/*static*/
+PluginGroup* PluginGroup::FromWebPluginInfo(const WebPluginInfo& wpi) {
+ // Create a matcher from the name of this plugin.
+ return new PluginGroup(wpi.name, wpi.name, std::string(),
+ GetIdentifier(wpi));
+}
+
+bool PluginGroup::Match(const WebPluginInfo& plugin) const {
+ if (name_matcher_.empty()) {
+ return false;
+ }
+
+ // Look for the name matcher anywhere in the plugin name.
+ if (plugin.name.find(name_matcher_) == string16::npos) {
+ return false;
+ }
+
+ if (version_ranges_.empty()) {
+ return true;
+ }
+
+ // There's at least one version range, the plugin's version must be in it.
+ scoped_ptr<Version> plugin_version(
+ Version::GetVersionFromString(UTF16ToWide(plugin.version)));
+ if (plugin_version.get() == NULL) {
+ // No version could be extracted, assume we don't match the range.
+ return false;
+ }
+
+ // Match if the plugin is contained in any of the defined VersionRanges.
+ for (size_t i = 0; i < version_ranges_.size(); ++i) {
+ if (IsVersionInRange(*plugin_version, version_ranges_[i])) {
+ return true;
+ }
+ }
+ // None of the VersionRanges matched.
+ return false;
+}
+
+/* static */
+Version* PluginGroup::CreateVersionFromString(const string16& version_string) {
+ // Remove spaces and ')' from the version string,
+ // Replace any instances of 'r', ',' or '(' with a dot.
+ std::wstring version = UTF16ToWide(version_string);
+ RemoveChars(version, L") ", &version);
+ std::replace(version.begin(), version.end(), 'r', '.');
+ std::replace(version.begin(), version.end(), ',', '.');
+ std::replace(version.begin(), version.end(), '(', '.');
+
+ return Version::GetVersionFromString(version);
+}
+
+void PluginGroup::UpdateActivePlugin(const WebPluginInfo& plugin) {
+ // A group is enabled if any of the files are enabled.
+ if (plugin.enabled) {
+ if (!enabled_) {
+ // If this is the first enabled plugin, use its description.
+ enabled_ = true;
+ UpdateDescriptionAndVersion(plugin);
+ }
+ } else {
+ // If this is the first plugin and it's disabled,
+ // use its description for now.
+ if (description_.empty())
+ UpdateDescriptionAndVersion(plugin);
+ }
+}
+
+void PluginGroup::UpdateDescriptionAndVersion(const WebPluginInfo& plugin) {
+ description_ = plugin.desc;
+ if (Version* new_version = CreateVersionFromString(plugin.version))
+ version_.reset(new_version);
+ else
+ version_.reset(Version::GetVersionFromString("0"));
+}
+
+void PluginGroup::AddPlugin(const WebPluginInfo& plugin, int position) {
+ // Check if this group already contains this plugin.
+ for (size_t i = 0; i < web_plugin_infos_.size(); ++i) {
+ if (web_plugin_infos_[i].name == plugin.name &&
+ web_plugin_infos_[i].version == plugin.version &&
+ FilePath::CompareEqualIgnoreCase(web_plugin_infos_[i].path.value(),
+ plugin.path.value())) {
+ return;
+ }
+ }
+ web_plugin_infos_.push_back(plugin);
+ // The position of this plugin relative to the global list of plugins.
+ web_plugin_positions_.push_back(position);
+ UpdateActivePlugin(plugin);
+}
+
+string16 PluginGroup::GetGroupName() const {
+ if (!group_name_.empty())
+ return group_name_;
+ DCHECK_EQ(1u, web_plugin_infos_.size());
+ FilePath::StringType path =
+ web_plugin_infos_[0].path.BaseName().RemoveExtension().value();
+#if defined(OS_POSIX)
+ return UTF8ToUTF16(path);
+#elif defined(OS_WIN)
+ return WideToUTF16(path);
+#endif
+}
+
+DictionaryValue* PluginGroup::GetSummary() const {
+ DictionaryValue* result = new DictionaryValue();
+ result->SetString("name", GetGroupName());
+ result->SetBoolean("enabled", enabled_);
+ return result;
+}
+
+DictionaryValue* PluginGroup::GetDataForUI() const {
+ string16 name = GetGroupName();
+ DictionaryValue* result = new DictionaryValue();
+ result->SetString("name", name);
+ result->SetString("description", description_);
+ result->SetString("version", version_->GetString());
+ result->SetString("update_url", update_url_);
+ result->SetBoolean("critical", IsVulnerable());
+
+ bool group_disabled_by_policy = IsPluginNameDisabledByPolicy(name);
+ ListValue* plugin_files = new ListValue();
+ bool all_plugins_disabled_by_policy = true;
+ for (size_t i = 0; i < web_plugin_infos_.size(); ++i) {
+ const WebPluginInfo& web_plugin = web_plugin_infos_[i];
+ int priority = web_plugin_positions_[i];
+ DictionaryValue* plugin_file = new DictionaryValue();
+ plugin_file->SetString("name", web_plugin.name);
+ plugin_file->SetString("description", web_plugin.desc);
+ plugin_file->SetString("path", web_plugin.path.value());
+ plugin_file->SetString("version", web_plugin.version);
+ bool plugin_disabled_by_policy = group_disabled_by_policy ||
+ IsPluginNameDisabledByPolicy(web_plugin.name);
+ if (plugin_disabled_by_policy) {
+ plugin_file->SetString("enabledMode", "disabledByPolicy");
+ } else {
+ all_plugins_disabled_by_policy = false;
+ plugin_file->SetString("enabledMode",
+ web_plugin.enabled ? "enabled" : "disabledByUser");
+ }
+ plugin_file->SetInteger("priority", priority);
+
+ ListValue* mime_types = new ListValue();
+ for (std::vector<WebPluginMimeType>::const_iterator type_it =
+ web_plugin.mime_types.begin();
+ type_it != web_plugin.mime_types.end();
+ ++type_it) {
+ DictionaryValue* mime_type = new DictionaryValue();
+ mime_type->SetString("mimeType", type_it->mime_type);
+ mime_type->SetString("description", type_it->description);
+
+ ListValue* file_extensions = new ListValue();
+ for (std::vector<std::string>::const_iterator ext_it =
+ type_it->file_extensions.begin();
+ ext_it != type_it->file_extensions.end();
+ ++ext_it) {
+ file_extensions->Append(new StringValue(*ext_it));
+ }
+ mime_type->Set("fileExtensions", file_extensions);
+
+ mime_types->Append(mime_type);
+ }
+ plugin_file->Set("mimeTypes", mime_types);
+
+ plugin_files->Append(plugin_file);
+ }
+
+ if (group_disabled_by_policy || all_plugins_disabled_by_policy) {
+ result->SetString("enabledMode", "disabledByPolicy");
+ } else {
+ result->SetString("enabledMode", enabled_ ? "enabled" : "disabledByUser");
+ }
+ result->Set("plugin_files", plugin_files);
+
+ return result;
+}
+
+/*static*/
+bool PluginGroup::IsVersionInRange(const Version& version,
+ const VersionRange& range) {
+ DCHECK(range.low.get() != NULL || range.high.get() == NULL)
+ << "Lower bound of version range must be defined.";
+ return (range.low.get() == NULL && range.high.get() == NULL) ||
+ (range.low->CompareTo(version) <= 0 &&
+ (range.high.get() == NULL || range.high->CompareTo(version) > 0));
+}
+
+/*static*/
+bool PluginGroup::IsPluginOutdated(const Version& plugin_version,
+ const VersionRange& version_range) {
+ if (IsVersionInRange(plugin_version, version_range)) {
+ if (version_range.min.get() &&
+ plugin_version.CompareTo(*version_range.min) < 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Returns true if the latest version of this plugin group is vulnerable.
+bool PluginGroup::IsVulnerable() const {
+ for (size_t i = 0; i < version_ranges_.size(); ++i) {
+ if (IsPluginOutdated(*version_, version_ranges_[i]))
+ return true;
+ }
+ return false;
+}
+
+void PluginGroup::DisableOutdatedPlugins() {
+ description_ = string16();
+ enabled_ = false;
+
+ for (std::vector<WebPluginInfo>::iterator it =
+ web_plugin_infos_.begin();
+ it != web_plugin_infos_.end(); ++it) {
+ scoped_ptr<Version> version(CreateVersionFromString(it->version));
+ if (version.get()) {
+ for (size_t i = 0; i < version_ranges_.size(); ++i) {
+ if (IsPluginOutdated(*version, version_ranges_[i])) {
+ it->enabled = false;
+ NPAPI::PluginList::Singleton()->DisablePlugin(it->path);
+ }
+ }
+ }
+ UpdateActivePlugin(*it);
+ }
+}
+
+void PluginGroup::Enable(bool enable) {
+ bool enabled_plugin_exists = false;
+ for (std::vector<WebPluginInfo>::iterator it =
+ web_plugin_infos_.begin();
+ it != web_plugin_infos_.end(); ++it) {
+ if (enable && !IsPluginNameDisabledByPolicy(it->name)) {
+ NPAPI::PluginList::Singleton()->EnablePlugin(it->path);
+ it->enabled = true;
+ enabled_plugin_exists = true;
+ } else {
+ it->enabled = false;
+ NPAPI::PluginList::Singleton()->DisablePlugin(it->path);
+ }
+ }
+ enabled_ = enabled_plugin_exists;
+}
diff --git a/webkit/glue/plugins/plugin_group.h b/webkit/glue/plugins/plugin_group.h
new file mode 100644
index 0000000..5098787
--- /dev/null
+++ b/webkit/glue/plugins/plugin_group.h
@@ -0,0 +1,205 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PLUGIN_GROUP_H_
+#define WEBKIT_GLUE_PLUGINS_PLUGIN_GROUP_H_
+#pragma once
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/scoped_ptr.h"
+#include "base/string16.h"
+
+class DictionaryValue;
+class FilePath;
+class Version;
+struct WebPluginInfo;
+
+namespace NPAPI {
+class PluginList;
+};
+
+// Hard-coded version ranges for plugin groups.
+struct VersionRangeDefinition {
+ // Matcher for lowest version matched by this range (inclusive). May be empty
+ // to match everything iff |version_matcher_high| is also empty.
+ const char* version_matcher_low;
+ // Matcher for highest version matched by this range (exclusive). May be empty
+ // to match anything higher than |version_matcher_low|.
+ const char* version_matcher_high;
+ const char* min_version; // Minimum secure version.
+};
+
+// Hard-coded definitions of plugin groups.
+struct PluginGroupDefinition {
+ const char* identifier; // Unique identifier for this group.
+ const char* name; // Name of this group.
+ const char* name_matcher; // Substring matcher for the plugin name.
+ const VersionRangeDefinition* versions; // List of version ranges.
+ const size_t num_versions; // Size of the array |versions| points to.
+ const char* update_url; // Location of latest secure version.
+};
+
+// Run-time structure to hold version range information.
+struct VersionRange {
+ public:
+ explicit VersionRange(VersionRangeDefinition definition);
+ VersionRange(const VersionRange& other);
+ VersionRange& operator=(const VersionRange& other);
+ ~VersionRange();
+
+ std::string low_str;
+ std::string high_str;
+ std::string min_str;
+ scoped_ptr<Version> low;
+ scoped_ptr<Version> high;
+ scoped_ptr<Version> min;
+ private:
+ void InitFrom(const VersionRange& other);
+};
+
+// A PluginGroup can match a range of versions of a specific plugin (as defined
+// by matching a substring of its name).
+// It contains all WebPluginInfo structs (at least one) matching its definition.
+// In addition, it knows about a security "baseline", i.e. the minimum version
+// of a plugin that is needed in order not to exhibit known security
+// vulnerabilities.
+
+class PluginGroup {
+ public:
+ // Used by about:plugins to disable Reader plugin when internal PDF viewer is
+ // enabled.
+ static const char* kAdobeReaderGroupName;
+
+ PluginGroup(const PluginGroup& other);
+
+ ~PluginGroup();
+
+ PluginGroup& operator=(const PluginGroup& other);
+
+ // Configures the set of plugin name patterns for disabling plugins via
+ // enterprise configuration management.
+ static void SetPolicyDisabledPluginPatterns(const std::set<string16>& set);
+
+ // Tests to see if a plugin is on the blacklist using its name as
+ // the lookup key.
+ static bool IsPluginNameDisabledByPolicy(const string16& plugin_name);
+
+ // Tests to see if a plugin is on the blacklist using its path as
+ // the lookup key.
+ static bool IsPluginPathDisabledByPolicy(const FilePath& plugin_path);
+
+ // Returns true if the given plugin matches this group.
+ bool Match(const WebPluginInfo& plugin) const;
+
+ // Adds the given plugin to this group. Provide the position of the
+ // plugin as given by PluginList so we can display its priority.
+ void AddPlugin(const WebPluginInfo& plugin, int position);
+
+ // Enables/disables this group. This enables/disables all plugins in the
+ // group.
+ void Enable(bool enable);
+
+ // Returns whether the plugin group is enabled or not.
+ bool Enabled() const { return enabled_; }
+
+ // Returns a unique identifier for this group, if one is defined, or the empty
+ // string otherwise.
+ const std::string& identifier() const { return identifier_; }
+
+ // Returns this group's name, or the filename without extension if the name
+ // is empty.
+ string16 GetGroupName() const;
+
+ // Returns the description of the highest-priority plug-in in the group.
+ const string16& description() const { return description_; }
+
+ // Returns a DictionaryValue with data to display in the UI.
+ DictionaryValue* GetDataForUI() const;
+
+ // Returns a DictionaryValue with data to save in the preferences.
+ DictionaryValue* GetSummary() const;
+
+ // Returns the update URL.
+ std::string GetUpdateURL() const { return update_url_; }
+
+ // Returns true if the highest-priority plugin in this group has known
+ // security problems.
+ bool IsVulnerable() const;
+
+ // Disables all plugins in this group that are older than the
+ // minimum version.
+ void DisableOutdatedPlugins();
+
+ // Parse a version string as used by a plug-in. This method is more lenient
+ // in accepting weird version strings than Version::GetFromString().
+ static Version* CreateVersionFromString(const string16& version_string);
+
+ private:
+ typedef std::map<std::string, PluginGroup*> PluginMap;
+
+ friend class NPAPI::PluginList;
+ friend class PluginGroupTest;
+ friend class TableModelArrayControllerTest;
+ friend class PluginExceptionsTableModelTest;
+
+ // Generates the (short) identifier string for the given plugin.
+ static std::string GetIdentifier(const WebPluginInfo& wpi);
+
+ // Generates the long identifier (based on the full file path) for the given
+ // plugin, to be called when the short identifier is not unique.
+ static std::string GetLongIdentifier(const WebPluginInfo& wpi);
+
+ // Creates a PluginGroup from a PluginGroupDefinition. The caller takes
+ // ownership of the created PluginGroup.
+ static PluginGroup* FromPluginGroupDefinition(
+ const PluginGroupDefinition& definition);
+
+ // Creates a PluginGroup from a WebPluginInfo. The caller takes ownership of
+ // the created PluginGroup.
+ static PluginGroup* FromWebPluginInfo(const WebPluginInfo& wpi);
+
+ // Returns |true| if |version| is contained in [low, high) of |range|.
+ static bool IsVersionInRange(const Version& version,
+ const VersionRange& range);
+
+ // Returns |true| iff |plugin_version| is both contained in |version_range|
+ // and declared outdated (== vulnerable) by it.
+ static bool IsPluginOutdated(const Version& plugin_version,
+ const VersionRange& version_range);
+
+ PluginGroup(const string16& group_name,
+ const string16& name_matcher,
+ const std::string& update_url,
+ const std::string& identifier);
+
+ void InitFrom(const PluginGroup& other);
+
+ // Set the description and version for this plugin group from the
+ // given plug-in.
+ void UpdateDescriptionAndVersion(const WebPluginInfo& plugin);
+
+ // Updates the active plugin in the group. The active plugin is the first
+ // enabled one, or if all plugins are disabled, simply the first one.
+ void UpdateActivePlugin(const WebPluginInfo& plugin);
+
+ static std::set<string16>* policy_disabled_plugin_patterns_;
+
+ std::string identifier_;
+ string16 group_name_;
+ string16 name_matcher_;
+ string16 description_;
+ std::string update_url_;
+ bool enabled_;
+ std::vector<VersionRange> version_ranges_;
+ scoped_ptr<Version> version_;
+ std::vector<WebPluginInfo> web_plugin_infos_;
+ std::vector<int> web_plugin_positions_;
+};
+
+#endif // WEBKIT_GLUE_PLUGINS_PLUGIN_GROUP_H_
diff --git a/webkit/glue/plugins/plugin_group_unittest.cc b/webkit/glue/plugins/plugin_group_unittest.cc
new file mode 100644
index 0000000..31dee1e
--- /dev/null
+++ b/webkit/glue/plugins/plugin_group_unittest.cc
@@ -0,0 +1,224 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_group.h"
+
+#include <string>
+#include <vector>
+
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "base/version.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/glue/plugins/webplugininfo.h"
+#include "webkit/glue/plugins/plugin_list.h"
+
+static const VersionRangeDefinition kPluginVersionRange[] = {
+ { "", "", "3.0.44" }
+};
+static const VersionRangeDefinition kPlugin3VersionRange[] = {
+ { "0", "4", "3.0.44" }
+};
+static const VersionRangeDefinition kPlugin4VersionRange[] = {
+ { "4", "5", "4.0.44" }
+};
+static const VersionRangeDefinition kPlugin34VersionRange[] = {
+ { "0", "4", "3.0.44" },
+ { "4", "5", "4.0.44" }
+};
+
+static const PluginGroupDefinition kPluginDef = {
+ "myplugin", "MyPlugin", "MyPlugin", kPluginVersionRange, 1,
+ "http://latest/" };
+static const PluginGroupDefinition kPluginDef3 = {
+ "myplugin-3", "MyPlugin 3", "MyPlugin", kPlugin3VersionRange, 1,
+ "http://latest" };
+static const PluginGroupDefinition kPluginDef4 = {
+ "myplugin-4", "MyPlugin 4", "MyPlugin", kPlugin4VersionRange, 1,
+ "http://latest" };
+static const PluginGroupDefinition kPluginDef34 = {
+ "myplugin-34", "MyPlugin 3/4", "MyPlugin", kPlugin34VersionRange, 2,
+ "http://latest" };
+static const PluginGroupDefinition kPluginDefNotVulnerable = {
+ "myplugin-latest", "MyPlugin", "MyPlugin", NULL, 0, "http://latest" };
+
+// name, path, version, desc, mime_types, enabled.
+static WebPluginInfo kPlugin2043 = WebPluginInfo(
+ ASCIIToUTF16("MyPlugin"), ASCIIToUTF16("2.0.43"),
+ ASCIIToUTF16("MyPlugin version 2.0.43"));
+static WebPluginInfo kPlugin3043 = WebPluginInfo(
+ ASCIIToUTF16("MyPlugin"), ASCIIToUTF16("3.0.43"),
+ ASCIIToUTF16("MyPlugin version 3.0.43"));
+static WebPluginInfo kPlugin3044 = WebPluginInfo(
+ ASCIIToUTF16("MyPlugin"), ASCIIToUTF16("3.0.44"),
+ ASCIIToUTF16("MyPlugin version 3.0.44"));
+static WebPluginInfo kPlugin3045 = WebPluginInfo(
+ ASCIIToUTF16("MyPlugin"), ASCIIToUTF16("3.0.45"),
+ ASCIIToUTF16("MyPlugin version 3.0.45"));
+static WebPluginInfo kPlugin4043 = WebPluginInfo(
+ ASCIIToUTF16("MyPlugin"), ASCIIToUTF16("4.0.43"),
+ ASCIIToUTF16("MyPlugin version 4.0.43"));
+
+class PluginGroupTest : public testing::Test {
+ public:
+ static PluginGroup* CreatePluginGroup(
+ const PluginGroupDefinition& definition) {
+ return PluginGroup::FromPluginGroupDefinition(definition);
+ }
+ static PluginGroup* CreatePluginGroup(const WebPluginInfo& wpi) {
+ return PluginGroup::FromWebPluginInfo(wpi);
+ }
+ protected:
+ virtual void TearDown() {
+ PluginGroup::SetPolicyDisabledPluginPatterns(std::set<string16>());
+ }
+};
+
+TEST(PluginGroupTest, PluginGroupMatch) {
+ scoped_ptr<PluginGroup> group(PluginGroupTest::CreatePluginGroup(
+ kPluginDef3));
+ EXPECT_TRUE(group->Match(kPlugin3045));
+ group->AddPlugin(kPlugin3045, 0);
+ EXPECT_FALSE(group->IsVulnerable());
+}
+
+TEST(PluginGroupTest, PluginGroupMatchCorrectVersion) {
+ scoped_ptr<PluginGroup> group(PluginGroupTest::CreatePluginGroup(
+ kPluginDef3));
+ EXPECT_TRUE(group->Match(kPlugin2043));
+ EXPECT_TRUE(group->Match(kPlugin3043));
+ EXPECT_FALSE(group->Match(kPlugin4043));
+
+ group.reset(PluginGroupTest::CreatePluginGroup(kPluginDef4));
+ EXPECT_FALSE(group->Match(kPlugin2043));
+ EXPECT_FALSE(group->Match(kPlugin3043));
+ EXPECT_TRUE(group->Match(kPlugin4043));
+
+ group.reset(PluginGroupTest::CreatePluginGroup(kPluginDef34));
+ EXPECT_TRUE(group->Match(kPlugin2043));
+ EXPECT_TRUE(group->Match(kPlugin3043));
+ EXPECT_TRUE(group->Match(kPlugin4043));
+}
+
+TEST(PluginGroupTest, PluginGroupDescription) {
+ string16 desc3043(ASCIIToUTF16("MyPlugin version 3.0.43"));
+ string16 desc3045(ASCIIToUTF16("MyPlugin version 3.0.45"));
+
+ PluginGroupDefinition plugindefs[] = { kPluginDef3, kPluginDef34 };
+ for (size_t i = 0; i < 2; ++i) {
+ WebPluginInfo plugin3043(kPlugin3043);
+ WebPluginInfo plugin3045(kPlugin3045);
+ {
+ scoped_ptr<PluginGroup> group(PluginGroupTest::CreatePluginGroup(
+ plugindefs[i]));
+ EXPECT_TRUE(group->Match(plugin3043));
+ group->AddPlugin(plugin3043, 0);
+ EXPECT_EQ(desc3043, group->description());
+ EXPECT_TRUE(group->IsVulnerable());
+ EXPECT_TRUE(group->Match(plugin3045));
+ group->AddPlugin(plugin3045, 1);
+ EXPECT_EQ(desc3043, group->description());
+ EXPECT_TRUE(group->IsVulnerable());
+ }
+
+ {
+ // Disable the first plugin.
+ plugin3043.enabled = false;
+ scoped_ptr<PluginGroup> group(PluginGroupTest::CreatePluginGroup(
+ plugindefs[i]));
+ EXPECT_TRUE(group->Match(plugin3043));
+ group->AddPlugin(plugin3043, 0);
+ EXPECT_EQ(desc3043, group->description());
+ EXPECT_TRUE(group->IsVulnerable());
+ EXPECT_FALSE(group->Enabled());
+ EXPECT_TRUE(group->Match(plugin3045));
+ group->AddPlugin(plugin3045, 1);
+ EXPECT_EQ(desc3045, group->description());
+ EXPECT_FALSE(group->IsVulnerable());
+ }
+
+ {
+ // Disable the second plugin.
+ plugin3045.enabled = false;
+ scoped_ptr<PluginGroup> group(PluginGroupTest::CreatePluginGroup(
+ plugindefs[i]));
+ EXPECT_TRUE(group->Match(plugin3043));
+ group->AddPlugin(plugin3043, 1);
+ EXPECT_EQ(desc3043, group->description());
+ EXPECT_TRUE(group->IsVulnerable());
+ EXPECT_TRUE(group->Match(plugin3045));
+ group->AddPlugin(plugin3045, 0);
+ EXPECT_EQ(desc3043, group->description());
+ EXPECT_TRUE(group->IsVulnerable());
+ }
+ }
+}
+
+TEST(PluginGroupTest, PluginGroupDefinition) {
+ const PluginGroupDefinition* definitions =
+ NPAPI::PluginList::GetPluginGroupDefinitions();
+ for (size_t i = 0;
+ i < NPAPI::PluginList::GetPluginGroupDefinitionsSize();
+ ++i) {
+ scoped_ptr<PluginGroup> def_group(
+ PluginGroupTest::CreatePluginGroup(definitions[i]));
+ ASSERT_TRUE(def_group.get() != NULL);
+ EXPECT_FALSE(def_group->Match(kPlugin2043));
+ }
+}
+
+TEST(PluginGroupTest, DisableOutdated) {
+ PluginGroupDefinition plugindefs[] = { kPluginDef3, kPluginDef34 };
+ for (size_t i = 0; i < 2; ++i) {
+ scoped_ptr<PluginGroup> group(PluginGroupTest::CreatePluginGroup(
+ plugindefs[i]));
+ group->AddPlugin(kPlugin3043, 0);
+ group->AddPlugin(kPlugin3045, 1);
+ EXPECT_EQ(ASCIIToUTF16("MyPlugin version 3.0.43"), group->description());
+ EXPECT_TRUE(group->IsVulnerable());
+
+ group->DisableOutdatedPlugins();
+ EXPECT_EQ(ASCIIToUTF16("MyPlugin version 3.0.45"), group->description());
+ EXPECT_FALSE(group->IsVulnerable());
+ }
+}
+
+TEST(PluginGroupTest, VersionExtraction) {
+ // Some real-world plugin versions (spaces, commata, parentheses, 'r', oh my)
+ const char* versions[][2] = {
+ { "7.6.6 (1671)", "7.6.6.1671" }, // Quicktime
+ { "2, 0, 0, 254", "2.0.0.254" }, // DivX
+ { "3, 0, 0, 0", "3.0.0.0" }, // Picasa
+ { "1, 0, 0, 1", "1.0.0.1" }, // Earth
+ { "10,0,45,2", "10.0.45.2" }, // Flash
+ { "11.5.7r609", "11.5.7.609"} // Shockwave
+ };
+
+ for (size_t i = 0; i < arraysize(versions); i++) {
+ const WebPluginInfo plugin = WebPluginInfo(
+ ASCIIToUTF16("Blah Plugin"), ASCIIToUTF16(versions[i][0]), string16());
+ scoped_ptr<PluginGroup> group(PluginGroupTest::CreatePluginGroup(plugin));
+ EXPECT_TRUE(group->Match(plugin));
+ group->AddPlugin(plugin, 0);
+ scoped_ptr<DictionaryValue> data(group->GetDataForUI());
+ std::string version;
+ data->GetString("version", &version);
+ EXPECT_EQ(versions[i][1], version);
+ }
+}
+
+TEST(PluginGroupTest, DisabledByPolicy) {
+ std::set<string16> disabled_plugins;
+ disabled_plugins.insert(ASCIIToUTF16("Disable this!"));
+ disabled_plugins.insert(ASCIIToUTF16("*Google*"));
+ PluginGroup::SetPolicyDisabledPluginPatterns(disabled_plugins);
+
+ EXPECT_FALSE(PluginGroup::IsPluginNameDisabledByPolicy(ASCIIToUTF16("42")));
+ EXPECT_TRUE(PluginGroup::IsPluginNameDisabledByPolicy(
+ ASCIIToUTF16("Disable this!")));
+ EXPECT_TRUE(PluginGroup::IsPluginNameDisabledByPolicy(
+ ASCIIToUTF16("Google Earth")));
+}
diff --git a/webkit/glue/plugins/plugin_host.cc b/webkit/glue/plugins/plugin_host.cc
new file mode 100644
index 0000000..28aba02
--- /dev/null
+++ b/webkit/glue/plugins/plugin_host.cc
@@ -0,0 +1,1111 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_host.h"
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/string_piece.h"
+#include "base/string_util.h"
+#if defined(OS_MACOSX)
+#include "base/sys_info.h"
+#endif
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "net/base/net_util.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+#include "third_party/npapi/bindings/npruntime.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebBindings.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebKit.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/glue/plugins/default_plugin_shared.h"
+#include "webkit/glue/plugins/npapi_extension_thunk.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/glue/plugins/plugin_stream_url.h"
+#include "webkit/glue/plugins/webplugin_delegate.h"
+#include "webkit/glue/plugins/webplugininfo.h"
+
+using WebKit::WebBindings;
+
+// Finds a PluginInstance from an NPP.
+// The caller must take a reference if needed.
+static NPAPI::PluginInstance* FindInstance(NPP id) {
+ if (id == NULL) {
+ return NULL;
+ }
+ return reinterpret_cast<NPAPI::PluginInstance*>(id->ndata);
+}
+
+#if defined(OS_MACOSX)
+// Returns true if the OS supports shared accelerated surfaces via IOSurface.
+// This is true on Snow Leopard and higher.
+static bool SupportsSharingAcceleratedSurfaces() {
+ int32 major, minor, bugfix;
+ base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix);
+ return major > 10 || (major == 10 && minor > 5);
+}
+#endif
+
+namespace NPAPI {
+
+scoped_refptr<PluginHost> PluginHost::singleton_;
+
+PluginHost::PluginHost() {
+ InitializeHostFuncs();
+}
+
+PluginHost::~PluginHost() {
+}
+
+PluginHost *PluginHost::Singleton() {
+ if (singleton_.get() == NULL) {
+ singleton_ = new PluginHost();
+ }
+
+ DCHECK(singleton_.get() != NULL);
+ return singleton_;
+}
+
+void PluginHost::InitializeHostFuncs() {
+ memset(&host_funcs_, 0, sizeof(host_funcs_));
+ host_funcs_.size = sizeof(host_funcs_);
+ host_funcs_.version = (NP_VERSION_MAJOR << 8) | (NP_VERSION_MINOR);
+
+ // The "basic" functions
+ host_funcs_.geturl = &NPN_GetURL;
+ host_funcs_.posturl = &NPN_PostURL;
+ host_funcs_.requestread = &NPN_RequestRead;
+ host_funcs_.newstream = &NPN_NewStream;
+ host_funcs_.write = &NPN_Write;
+ host_funcs_.destroystream = &NPN_DestroyStream;
+ host_funcs_.status = &NPN_Status;
+ host_funcs_.uagent = &NPN_UserAgent;
+ host_funcs_.memalloc = &NPN_MemAlloc;
+ host_funcs_.memfree = &NPN_MemFree;
+ host_funcs_.memflush = &NPN_MemFlush;
+ host_funcs_.reloadplugins = &NPN_ReloadPlugins;
+
+ // We don't implement java yet
+ host_funcs_.getJavaEnv = &NPN_GetJavaEnv;
+ host_funcs_.getJavaPeer = &NPN_GetJavaPeer;
+
+ // Advanced functions we implement
+ host_funcs_.geturlnotify = &NPN_GetURLNotify;
+ host_funcs_.posturlnotify = &NPN_PostURLNotify;
+ host_funcs_.getvalue = &NPN_GetValue;
+ host_funcs_.setvalue = &NPN_SetValue;
+ host_funcs_.invalidaterect = &NPN_InvalidateRect;
+ host_funcs_.invalidateregion = &NPN_InvalidateRegion;
+ host_funcs_.forceredraw = &NPN_ForceRedraw;
+
+ // These come from the Javascript Engine
+ host_funcs_.getstringidentifier = WebBindings::getStringIdentifier;
+ host_funcs_.getstringidentifiers = WebBindings::getStringIdentifiers;
+ host_funcs_.getintidentifier = WebBindings::getIntIdentifier;
+ host_funcs_.identifierisstring = WebBindings::identifierIsString;
+ host_funcs_.utf8fromidentifier = WebBindings::utf8FromIdentifier;
+ host_funcs_.intfromidentifier = WebBindings::intFromIdentifier;
+ host_funcs_.createobject = WebBindings::createObject;
+ host_funcs_.retainobject = WebBindings::retainObject;
+ host_funcs_.releaseobject = WebBindings::releaseObject;
+ host_funcs_.invoke = WebBindings::invoke;
+ host_funcs_.invokeDefault = WebBindings::invokeDefault;
+ host_funcs_.evaluate = WebBindings::evaluate;
+ host_funcs_.getproperty = WebBindings::getProperty;
+ host_funcs_.setproperty = WebBindings::setProperty;
+ host_funcs_.removeproperty = WebBindings::removeProperty;
+ host_funcs_.hasproperty = WebBindings::hasProperty;
+ host_funcs_.hasmethod = WebBindings::hasMethod;
+ host_funcs_.releasevariantvalue = WebBindings::releaseVariantValue;
+ host_funcs_.setexception = WebBindings::setException;
+ host_funcs_.pushpopupsenabledstate = NPN_PushPopupsEnabledState;
+ host_funcs_.poppopupsenabledstate = NPN_PopPopupsEnabledState;
+ host_funcs_.enumerate = WebBindings::enumerate;
+ host_funcs_.pluginthreadasynccall = NPN_PluginThreadAsyncCall;
+ host_funcs_.construct = WebBindings::construct;
+ host_funcs_.getvalueforurl = NPN_GetValueForURL;
+ host_funcs_.setvalueforurl = NPN_SetValueForURL;
+ host_funcs_.getauthenticationinfo = NPN_GetAuthenticationInfo;
+ host_funcs_.scheduletimer = NPN_ScheduleTimer;
+ host_funcs_.unscheduletimer = NPN_UnscheduleTimer;
+ host_funcs_.popupcontextmenu = NPN_PopUpContextMenu;
+ host_funcs_.convertpoint = NPN_ConvertPoint;
+ host_funcs_.handleevent = NPN_HandleEvent;
+ host_funcs_.unfocusinstance = NPN_UnfocusInstance;
+ host_funcs_.urlredirectresponse = NPN_URLRedirectResponse;
+}
+
+void PluginHost::PatchNPNetscapeFuncs(NPNetscapeFuncs* overrides) {
+ // When running in the plugin process, we need to patch the NPN functions
+ // that the plugin calls to interact with NPObjects that we give. Otherwise
+ // the plugin will call the v8 NPN functions, which won't work since we have
+ // an NPObjectProxy and not a real v8 implementation.
+ if (overrides->invoke)
+ host_funcs_.invoke = overrides->invoke;
+
+ if (overrides->invokeDefault)
+ host_funcs_.invokeDefault = overrides->invokeDefault;
+
+ if (overrides->evaluate)
+ host_funcs_.evaluate = overrides->evaluate;
+
+ if (overrides->getproperty)
+ host_funcs_.getproperty = overrides->getproperty;
+
+ if (overrides->setproperty)
+ host_funcs_.setproperty = overrides->setproperty;
+
+ if (overrides->removeproperty)
+ host_funcs_.removeproperty = overrides->removeproperty;
+
+ if (overrides->hasproperty)
+ host_funcs_.hasproperty = overrides->hasproperty;
+
+ if (overrides->hasmethod)
+ host_funcs_.hasmethod = overrides->hasmethod;
+
+ if (overrides->setexception)
+ host_funcs_.setexception = overrides->setexception;
+
+ if (overrides->enumerate)
+ host_funcs_.enumerate = overrides->enumerate;
+}
+
+bool PluginHost::SetPostData(const char* buf,
+ uint32 length,
+ std::vector<std::string>* names,
+ std::vector<std::string>* values,
+ std::vector<char>* body) {
+ // Use a state table to do the parsing. Whitespace must be
+ // trimmed after the fact if desired. In our case, we actually
+ // don't care about the whitespace, because we're just going to
+ // pass this back into another POST. This function strips out the
+ // "Content-length" header and does not append it to the request.
+
+ //
+ // This parser takes action only on state changes.
+ //
+ // Transition table:
+ // : \n NULL Other
+ // 0 GetHeader 1 2 4 0
+ // 1 GetValue 1 0 3 1
+ // 2 GetData 2 2 3 2
+ // 3 DONE
+ // 4 ERR
+ //
+ enum { INPUT_COLON=0, INPUT_NEWLINE, INPUT_NULL, INPUT_OTHER };
+ enum { GETNAME, GETVALUE, GETDATA, DONE, ERR };
+ int statemachine[3][4] = { { GETVALUE, GETDATA, GETDATA, GETNAME },
+ { GETVALUE, GETNAME, DONE, GETVALUE },
+ { GETDATA, GETDATA, DONE, GETDATA } };
+ std::string name, value;
+ const char* ptr = static_cast<const char*>(buf);
+ const char* start = ptr;
+ int state = GETNAME; // initial state
+ bool done = false;
+ bool err = false;
+ do {
+ int input;
+
+ // Translate the current character into an input
+ // for the state table.
+ switch (*ptr) {
+ case ':' :
+ input = INPUT_COLON;
+ break;
+ case '\n':
+ input = INPUT_NEWLINE;
+ break;
+ case 0 :
+ input = INPUT_NULL;
+ break;
+ default :
+ input = INPUT_OTHER;
+ break;
+ }
+
+ int newstate = statemachine[state][input];
+
+ // Take action based on the new state.
+ if (state != newstate) {
+ switch (newstate) {
+ case GETNAME:
+ // Got a value.
+ value = std::string(start, ptr - start);
+ TrimWhitespace(value, TRIM_ALL, &value);
+ // If the name field is empty, we'll skip this header
+ // but we won't error out.
+ if (!name.empty() && name != "content-length") {
+ names->push_back(name);
+ values->push_back(value);
+ }
+ start = ptr + 1;
+ break;
+ case GETVALUE:
+ // Got a header.
+ name = StringToLowerASCII(std::string(start, ptr - start));
+ TrimWhitespace(name, TRIM_ALL, &name);
+ start = ptr + 1;
+ break;
+ case GETDATA: {
+ // Finished headers, now get body
+ if (*ptr)
+ start = ptr + 1;
+ size_t previous_size = body->size();
+ size_t new_body_size = length - static_cast<int>(start - buf);
+ body->resize(previous_size + new_body_size);
+ if (!body->empty())
+ memcpy(&body->front() + previous_size, start, new_body_size);
+ done = true;
+ break;
+ }
+ case ERR:
+ // error
+ err = true;
+ done = true;
+ break;
+ }
+ }
+ state = newstate;
+ ptr++;
+ } while (!done);
+
+ return !err;
+}
+
+} // namespace NPAPI
+
+extern "C" {
+
+// Allocates memory from the host's memory space.
+void* NPN_MemAlloc(uint32_t size) {
+ scoped_refptr<NPAPI::PluginHost> host(NPAPI::PluginHost::Singleton());
+ if (host != NULL) {
+ // Note: We must use the same allocator/deallocator
+ // that is used by the javascript library, as some of the
+ // JS APIs will pass memory to the plugin which the plugin
+ // will attempt to free.
+ return malloc(size);
+ }
+ return NULL;
+}
+
+// Deallocates memory from the host's memory space
+void NPN_MemFree(void* ptr) {
+ scoped_refptr<NPAPI::PluginHost> host(NPAPI::PluginHost::Singleton());
+ if (host != NULL) {
+ if (ptr != NULL && ptr != reinterpret_cast<void*>(-1))
+ free(ptr);
+ }
+}
+
+// Requests that the host free a specified amount of memory.
+uint32_t NPN_MemFlush(uint32_t size) {
+ // This is not relevant on Windows; MAC specific
+ return size;
+}
+
+// This is for dynamic discovery of new plugins.
+// Should force a re-scan of the plugins directory to load new ones.
+void NPN_ReloadPlugins(NPBool reload_pages) {
+ WebKit::resetPluginCache(reload_pages ? true : false);
+}
+
+// Requests a range of bytes for a seekable stream.
+NPError NPN_RequestRead(NPStream* stream, NPByteRange* range_list) {
+ if (!stream || !range_list)
+ return NPERR_GENERIC_ERROR;
+
+ scoped_refptr<NPAPI::PluginInstance> plugin(
+ reinterpret_cast<NPAPI::PluginInstance*>(stream->ndata));
+ if (!plugin.get())
+ return NPERR_GENERIC_ERROR;
+
+ plugin->RequestRead(stream, range_list);
+ return NPERR_NO_ERROR;
+}
+
+// Generic form of GetURL for common code between GetURL and GetURLNotify.
+static NPError GetURLNotify(NPP id,
+ const char* url,
+ const char* target,
+ bool notify,
+ void* notify_data) {
+ if (!url)
+ return NPERR_INVALID_URL;
+
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (!plugin.get()) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ plugin->RequestURL(url, "GET", target, NULL, 0, notify, notify_data);
+ return NPERR_NO_ERROR;
+}
+
+// Requests creation of a new stream with the contents of the
+// specified URL; gets notification of the result.
+NPError NPN_GetURLNotify(NPP id,
+ const char* url,
+ const char* target,
+ void* notify_data) {
+ // This is identical to NPN_GetURL, but after finishing, the
+ // browser will call NPP_URLNotify to inform the plugin that
+ // it has completed.
+
+ // According to the NPAPI documentation, if target == _self
+ // or a parent to _self, the browser should return NPERR_INVALID_PARAM,
+ // because it can't notify the plugin once deleted. This is
+ // absolutely false; firefox doesn't do this, and Flash relies on
+ // being able to use this.
+
+ // Also according to the NPAPI documentation, we should return
+ // NPERR_INVALID_URL if the url requested is not valid. However,
+ // this would require that we synchronously start fetching the
+ // URL. That just isn't practical. As such, there really is
+ // no way to return this error. From looking at the Firefox
+ // implementation, it doesn't look like Firefox does this either.
+
+ return GetURLNotify(id, url, target, true, notify_data);
+}
+
+NPError NPN_GetURL(NPP id, const char* url, const char* target) {
+ // Notes:
+ // Request from the Plugin to fetch content either for the plugin
+ // or to be placed into a browser window.
+ //
+ // If target == null, the browser fetches content and streams to plugin.
+ // otherwise, the browser loads content into an existing browser frame.
+ // If the target is the window/frame containing the plugin, the plugin
+ // may be destroyed.
+ // If the target is _blank, a mailto: or news: url open content in a new
+ // browser window
+ // If the target is _self, no other instance of the plugin is created. The
+ // plugin continues to operate in its own window
+
+ return GetURLNotify(id, url, target, false, 0);
+}
+
+// Generic form of PostURL for common code between PostURL and PostURLNotify.
+static NPError PostURLNotify(NPP id,
+ const char* url,
+ const char* target,
+ uint32_t len,
+ const char* buf,
+ NPBool file,
+ bool notify,
+ void* notify_data) {
+ if (!url)
+ return NPERR_INVALID_URL;
+
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (!plugin.get()) {
+ NOTREACHED();
+ return NPERR_GENERIC_ERROR;
+ }
+
+ std::string post_file_contents;
+
+ if (file) {
+ // Post data to be uploaded from a file. This can be handled in two
+ // ways.
+ // 1. Read entire file and send the contents as if it was a post data
+ // specified in the argument
+ // 2. Send just the file details and read them in the browser at the
+ // time of sending the request.
+ // Approach 2 is more efficient but complicated. Approach 1 has a major
+ // drawback of sending potentially large data over two IPC hops. In a way
+ // 'large data over IPC' problem exists as it is in case of plugin giving
+ // the data directly instead of in a file.
+ // Currently we are going with the approach 1 to get the feature working.
+ // We can optimize this later with approach 2.
+
+ // TODO(joshia): Design a scheme to send a file descriptor instead of
+ // entire file contents across.
+
+ // Security alert:
+ // ---------------
+ // Here we are blindly uploading whatever file requested by a plugin.
+ // This is risky as someone could exploit a plugin to send private
+ // data in arbitrary locations.
+ // A malicious (non-sandboxed) plugin has unfeterred access to OS
+ // resources and can do this anyway without using browser's HTTP stack.
+ // FWIW, Firefox and Safari don't perform any security checks.
+
+ if (!buf)
+ return NPERR_FILE_NOT_FOUND;
+
+ std::string file_path_ascii(buf);
+ FilePath file_path;
+ static const char kFileUrlPrefix[] = "file:";
+ if (StartsWithASCII(file_path_ascii, kFileUrlPrefix, false)) {
+ GURL file_url(file_path_ascii);
+ DCHECK(file_url.SchemeIsFile());
+ net::FileURLToFilePath(file_url, &file_path);
+ } else {
+ file_path = FilePath::FromWStringHack(
+ base::SysNativeMBToWide(file_path_ascii));
+ }
+
+ base::PlatformFileInfo post_file_info = {0};
+ if (!file_util::GetFileInfo(file_path, &post_file_info) ||
+ post_file_info.is_directory)
+ return NPERR_FILE_NOT_FOUND;
+
+ if (!file_util::ReadFileToString(file_path, &post_file_contents))
+ return NPERR_FILE_NOT_FOUND;
+
+ buf = post_file_contents.c_str();
+ len = post_file_contents.size();
+ }
+
+ // The post data sent by a plugin contains both headers
+ // and post data. Example:
+ // Content-type: text/html
+ // Content-length: 200
+ //
+ // <200 bytes of content here>
+ //
+ // Unfortunately, our stream needs these broken apart,
+ // so we need to parse the data and set headers and data
+ // separately.
+ plugin->RequestURL(url, "POST", target, buf, len, notify, notify_data);
+ return NPERR_NO_ERROR;
+}
+
+NPError NPN_PostURLNotify(NPP id,
+ const char* url,
+ const char* target,
+ uint32_t len,
+ const char* buf,
+ NPBool file,
+ void* notify_data) {
+ return PostURLNotify(id, url, target, len, buf, file, true, notify_data);
+}
+
+NPError NPN_PostURL(NPP id,
+ const char* url,
+ const char* target,
+ uint32_t len,
+ const char* buf,
+ NPBool file) {
+ // POSTs data to an URL, either from a temp file or a buffer.
+ // If file is true, buf contains a temp file (which host will delete after
+ // completing), and len contains the length of the filename.
+ // If file is false, buf contains the data to send, and len contains the
+ // length of the buffer
+ //
+ // If target is null,
+ // server response is returned to the plugin
+ // If target is _current, _self, or _top,
+ // server response is written to the plugin window and plugin is unloaded.
+ // If target is _new or _blank,
+ // server response is written to a new browser window
+ // If target is an existing frame,
+ // server response goes to that frame.
+ //
+ // For protocols other than FTP
+ // file uploads must be line-end converted from \r\n to \n
+ //
+ // Note: you cannot specify headers (even a blank line) in a memory buffer,
+ // use NPN_PostURLNotify
+
+ return PostURLNotify(id, url, target, len, buf, file, false, 0);
+}
+
+NPError NPN_NewStream(NPP id,
+ NPMIMEType type,
+ const char* target,
+ NPStream** stream) {
+ // Requests creation of a new data stream produced by the plugin,
+ // consumed by the browser.
+ //
+ // Browser should put this stream into a window target.
+ //
+ // TODO: implement me
+ DVLOG(1) << "NPN_NewStream is not implemented yet.";
+ return NPERR_GENERIC_ERROR;
+}
+
+int32_t NPN_Write(NPP id, NPStream* stream, int32_t len, void* buffer) {
+ // Writes data to an existing Plugin-created stream.
+
+ // TODO: implement me
+ DVLOG(1) << "NPN_Write is not implemented yet.";
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError NPN_DestroyStream(NPP id, NPStream* stream, NPReason reason) {
+ // Destroys a stream (could be created by plugin or browser).
+ //
+ // Reasons:
+ // NPRES_DONE - normal completion
+ // NPRES_USER_BREAK - user terminated
+ // NPRES_NETWORK_ERROR - network error (all errors fit here?)
+ //
+ //
+
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin.get() == NULL) {
+ NOTREACHED();
+ return NPERR_GENERIC_ERROR;
+ }
+
+ return plugin->NPP_DestroyStream(stream, reason);
+}
+
+const char* NPN_UserAgent(NPP id) {
+#if defined(OS_WIN)
+ // Flash passes in a null id during the NP_initialize call. We need to
+ // default to the Mozilla user agent if we don't have an NPP instance or
+ // else Flash won't request windowless mode.
+ bool use_mozilla_user_agent = true;
+ if (id) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin.get() && !plugin->use_mozilla_user_agent())
+ use_mozilla_user_agent = false;
+ }
+
+ if (use_mozilla_user_agent)
+ return "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9a1) "
+ "Gecko/20061103 Firefox/2.0a1";
+#elif defined(OS_MACOSX)
+ // Silverlight 4 doesn't handle events correctly unless we claim to be Safari.
+ scoped_refptr<NPAPI::PluginInstance> plugin;
+ if (id)
+ plugin = FindInstance(id);
+ if (plugin.get()) {
+ WebPluginInfo plugin_info = plugin->plugin_lib()->plugin_info();
+ if (plugin_info.name == ASCIIToUTF16("Silverlight Plug-In") &&
+ StartsWith(plugin_info.version, ASCIIToUTF16("4."), false)) {
+ return "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-us) "
+ "AppleWebKit/534.1+ (KHTML, like Gecko) Version/5.0 Safari/533.16";
+ }
+ }
+#endif
+
+ return webkit_glue::GetUserAgent(GURL()).c_str();
+}
+
+void NPN_Status(NPP id, const char* message) {
+ // Displays a message on the status line of the browser window.
+
+ // TODO: implement me
+ DVLOG(1) << "NPN_Status is not implemented yet.";
+}
+
+void NPN_InvalidateRect(NPP id, NPRect *invalidRect) {
+ // Invalidates specified drawing area prior to repainting or refreshing a
+ // windowless plugin
+
+ // Before a windowless plugin can refresh part of its drawing area, it must
+ // first invalidate it. This function causes the NPP_HandleEvent method to
+ // pass an update event or a paint message to the plug-in. After calling
+ // this method, the plug-in recieves a paint message asynchronously.
+
+ // The browser redraws invalid areas of the document and any windowless
+ // plug-ins at regularly timed intervals. To force a paint message, the
+ // plug-in can call NPN_ForceRedraw after calling this method.
+
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin.get() && plugin->webplugin()) {
+ if (invalidRect) {
+#if defined(OS_WIN)
+ if (!plugin->windowless()) {
+ RECT rect = {0};
+ rect.left = invalidRect->left;
+ rect.right = invalidRect->right;
+ rect.top = invalidRect->top;
+ rect.bottom = invalidRect->bottom;
+ ::InvalidateRect(plugin->window_handle(), &rect, false);
+ return;
+ }
+#endif
+ gfx::Rect rect(invalidRect->left,
+ invalidRect->top,
+ invalidRect->right - invalidRect->left,
+ invalidRect->bottom - invalidRect->top);
+ plugin->webplugin()->InvalidateRect(rect);
+ } else {
+ plugin->webplugin()->Invalidate();
+ }
+ }
+}
+
+void NPN_InvalidateRegion(NPP id, NPRegion invalidRegion) {
+ // Invalidates a specified drawing region prior to repainting
+ // or refreshing a window-less plugin.
+ //
+ // Similar to NPN_InvalidateRect.
+
+ // TODO: this is overkill--add platform-specific region handling (at the
+ // very least, fetch the region's bounding box and pass it to InvalidateRect).
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ DCHECK(plugin.get() != NULL);
+ if (plugin.get() && plugin->webplugin())
+ plugin->webplugin()->Invalidate();
+}
+
+void NPN_ForceRedraw(NPP id) {
+ // Forces repaint for a windowless plug-in.
+ //
+ // We deliberately do not implement this; we don't want plugins forcing
+ // synchronous paints.
+}
+
+NPError NPN_GetValue(NPP id, NPNVariable variable, void* value) {
+ // Allows the plugin to query the browser for information
+ //
+ // Variables:
+ // NPNVxDisplay (unix only)
+ // NPNVxtAppContext (unix only)
+ // NPNVnetscapeWindow (win only) - Gets the native window on which the
+ // plug-in drawing occurs, returns HWND
+ // NPNVjavascriptEnabledBool: tells whether Javascript is enabled
+ // NPNVasdEnabledBool: tells whether SmartUpdate is enabled
+ // NPNVOfflineBool: tells whether offline-mode is enabled
+
+ NPError rv = NPERR_GENERIC_ERROR;
+
+ switch (static_cast<int>(variable)) {
+ case NPNVWindowNPObject: {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ NPObject *np_object = plugin->webplugin()->GetWindowScriptNPObject();
+ // Return value is expected to be retained, as
+ // described here:
+ // <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
+ if (np_object) {
+ WebBindings::retainObject(np_object);
+ void **v = (void **)value;
+ *v = np_object;
+ rv = NPERR_NO_ERROR;
+ } else {
+ NOTREACHED();
+ }
+ break;
+ }
+ case NPNVPluginElementNPObject: {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ NPObject *np_object = plugin->webplugin()->GetPluginElement();
+ // Return value is expected to be retained, as
+ // described here:
+ // <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess>
+ if (np_object) {
+ WebBindings::retainObject(np_object);
+ void** v = static_cast<void**>(value);
+ *v = np_object;
+ rv = NPERR_NO_ERROR;
+ } else {
+ NOTREACHED();
+ }
+ break;
+ }
+ #if !defined(OS_MACOSX) // OS X doesn't have windowed plugins.
+ case NPNVnetscapeWindow: {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (!plugin.get()) {
+ NOTREACHED();
+ return NPERR_GENERIC_ERROR;
+ }
+ gfx::PluginWindowHandle handle = plugin->window_handle();
+ *((void**)value) = (void*)handle;
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+ #endif
+ case NPNVjavascriptEnabledBool: {
+ // yes, JS is enabled.
+ *((void**)value) = (void*)1;
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+ #if defined(TOOLKIT_USES_GTK)
+ case NPNVToolkit:
+ // Tell them we are GTK2. (The alternative is GTK 1.2.)
+ *reinterpret_cast<int*>(value) = NPNVGtk2;
+ rv = NPERR_NO_ERROR;
+ break;
+
+ case NPNVSupportsXEmbedBool:
+ *reinterpret_cast<NPBool*>(value) = true;
+ rv = NPERR_NO_ERROR;
+ break;
+ #endif
+ case NPNVSupportsWindowless: {
+ NPBool* supports_windowless = reinterpret_cast<NPBool*>(value);
+ *supports_windowless = true;
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+ case NPNVprivateModeBool: {
+ NPBool* private_mode = reinterpret_cast<NPBool*>(value);
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ *private_mode = plugin->webplugin()->IsOffTheRecord();
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+ case default_plugin::kMissingPluginStatusStart +
+ default_plugin::MISSING_PLUGIN_AVAILABLE:
+ // fall through
+ case default_plugin::kMissingPluginStatusStart +
+ default_plugin::MISSING_PLUGIN_USER_STARTED_DOWNLOAD: {
+ // This is a hack for the default plugin to send notification to
+ // renderer. Even though we check if the plugin is the default plugin,
+ // we still need to worry about future standard change that may conflict
+ // with the variable definition, in order to avoid duplicate case clauses
+ // in this big switch statement.
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin->plugin_lib()->plugin_info().path.value() ==
+ kDefaultPluginLibraryName) {
+ plugin->webplugin()->OnMissingPluginStatus(
+ variable - default_plugin::kMissingPluginStatusStart);
+ }
+ break;
+ }
+ #if defined(OS_MACOSX)
+ case NPNVpluginDrawingModel: {
+ // return the drawing model that was negotiated when we initialized.
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ *reinterpret_cast<int*>(value) = plugin->drawing_model();
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+#ifndef NP_NO_QUICKDRAW
+ case NPNVsupportsQuickDrawBool: {
+ // We do not admit to supporting the QuickDraw drawing model. The logic
+ // here is that our QuickDraw plugin support is so rudimentary that we
+ // only want to use it as a fallback to keep plugins from crashing: if a
+ // plugin knows enough to ask, we want them to use CoreGraphics.
+ NPBool* supports_qd = reinterpret_cast<NPBool*>(value);
+ *supports_qd = false;
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+#endif
+ case NPNVsupportsCoreGraphicsBool:
+#ifndef NP_NO_CARBON
+ case NPNVsupportsCarbonBool:
+#endif
+ case NPNVsupportsCocoaBool: {
+ // we do support these drawing and event models.
+ NPBool* supports_model = reinterpret_cast<NPBool*>(value);
+ *supports_model = true;
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+ case NPNVsupportsCoreAnimationBool: {
+ // We only support the Core Animation model on 10.6 and higher
+ // TODO(stuartmorgan): Once existing CA plugins have implemented the
+ // invalidating version, remove support for this one.
+ NPBool* supports_model = reinterpret_cast<NPBool*>(value);
+ *supports_model = SupportsSharingAcceleratedSurfaces() ? true : false;
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+ case NPNVsupportsInvalidatingCoreAnimationBool: {
+ NPBool* supports_model = reinterpret_cast<NPBool*>(value);
+ *supports_model = true;
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+ case NPNVsupportsOpenGLBool: {
+ // This drawing model was never widely supported, and we don't plan to
+ // support it.
+ NPBool* supports_model = reinterpret_cast<NPBool*>(value);
+ *supports_model = false;
+ rv = NPERR_NO_ERROR;
+ break;
+ }
+ #endif // OS_MACOSX
+ case NPNVPepperExtensions:
+ // Available for any plugin that attempts to get it.
+ // If the plugin is not started in a Pepper implementation, it
+ // will likely fail when it tries to use any of the functions
+ // attached to the extension vector.
+ rv = NPAPI::GetPepperExtensionsFunctions(value);
+ break;
+ default:
+ DVLOG(1) << "NPN_GetValue(" << variable << ") is not implemented yet.";
+ break;
+ }
+ return rv;
+}
+
+NPError NPN_SetValue(NPP id, NPPVariable variable, void* value) {
+ // Allows the plugin to set various modes
+
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ switch(variable) {
+ case NPPVpluginWindowBool: {
+ // Sets windowless mode for display of the plugin
+ // Note: the documentation at
+ // http://developer.mozilla.org/en/docs/NPN_SetValue is wrong. When
+ // value is NULL, the mode is set to true. This is the same way Mozilla
+ // works.
+ plugin->set_windowless(value == 0);
+ return NPERR_NO_ERROR;
+ }
+ case NPPVpluginTransparentBool: {
+ // Sets transparent mode for display of the plugin
+ //
+ // Transparent plugins require the browser to paint the background
+ // before having the plugin paint. By default, windowless plugins
+ // are transparent. Making a windowless plugin opaque means that
+ // the plugin does not require the browser to paint the background.
+ bool mode = (value != 0);
+ plugin->set_transparent(mode);
+ return NPERR_NO_ERROR;
+ }
+ case NPPVjavascriptPushCallerBool:
+ // Specifies whether you are pushing or popping the JSContext off.
+ // the stack
+ // TODO: implement me
+ DVLOG(1) << "NPN_SetValue(NPPVJavascriptPushCallerBool) is not "
+ "implemented.";
+ return NPERR_GENERIC_ERROR;
+ case NPPVpluginKeepLibraryInMemory:
+ // Tells browser that plugin library should live longer than usual.
+ // TODO: implement me
+ DVLOG(1) << "NPN_SetValue(NPPVpluginKeepLibraryInMemory) is not "
+ "implemented.";
+ return NPERR_GENERIC_ERROR;
+ #if defined(OS_MACOSX)
+ case NPPVpluginDrawingModel: {
+ int model = reinterpret_cast<int>(value);
+ if (model == NPDrawingModelCoreGraphics ||
+ model == NPDrawingModelInvalidatingCoreAnimation ||
+ (model == NPDrawingModelCoreAnimation &&
+ SupportsSharingAcceleratedSurfaces())) {
+ plugin->set_drawing_model(static_cast<NPDrawingModel>(model));
+ return NPERR_NO_ERROR;
+ }
+ return NPERR_GENERIC_ERROR;
+ }
+ case NPPVpluginEventModel: {
+ // we support Carbon and Cocoa event models
+ int model = reinterpret_cast<int>(value);
+ switch (model) {
+#ifndef NP_NO_CARBON
+ case NPEventModelCarbon:
+#endif
+ case NPEventModelCocoa:
+ plugin->set_event_model(static_cast<NPEventModel>(model));
+ return NPERR_NO_ERROR;
+ break;
+ }
+ return NPERR_GENERIC_ERROR;
+ }
+ #endif
+ default:
+ // TODO: implement me
+ DVLOG(1) << "NPN_SetValue(" << variable << ") is not implemented.";
+ break;
+ }
+
+ NOTREACHED();
+ return NPERR_GENERIC_ERROR;
+}
+
+void* NPN_GetJavaEnv() {
+ // TODO: implement me
+ DVLOG(1) << "NPN_GetJavaEnv is not implemented.";
+ return NULL;
+}
+
+void* NPN_GetJavaPeer(NPP) {
+ // TODO: implement me
+ DVLOG(1) << "NPN_GetJavaPeer is not implemented.";
+ return NULL;
+}
+
+void NPN_PushPopupsEnabledState(NPP id, NPBool enabled) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin)
+ plugin->PushPopupsEnabledState(enabled ? true : false);
+}
+
+void NPN_PopPopupsEnabledState(NPP id) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin)
+ plugin->PopPopupsEnabledState();
+}
+
+void NPN_PluginThreadAsyncCall(NPP id,
+ void (*func)(void*),
+ void* user_data) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin)
+ plugin->PluginThreadAsyncCall(func, user_data);
+}
+
+NPError NPN_GetValueForURL(NPP id,
+ NPNURLVariable variable,
+ const char* url,
+ char** value,
+ uint32_t* len) {
+ if (!id)
+ return NPERR_INVALID_PARAM;
+
+ if (!url || !*url || !len)
+ return NPERR_INVALID_URL;
+
+ *len = 0;
+ std::string result;
+
+ switch (variable) {
+ case NPNURLVProxy: {
+ result = "DIRECT";
+ if (!webkit_glue::FindProxyForUrl(GURL((std::string(url))), &result))
+ return NPERR_GENERIC_ERROR;
+
+ break;
+ }
+ case NPNURLVCookie: {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (!plugin)
+ return NPERR_GENERIC_ERROR;
+
+ webkit_glue::WebPlugin* webplugin = plugin->webplugin();
+ if (!webplugin)
+ return NPERR_GENERIC_ERROR;
+
+ // Bypass third-party cookie blocking by using the url as the
+ // first_party_for_cookies.
+ GURL cookies_url((std::string(url)));
+ result = webplugin->GetCookies(cookies_url, cookies_url);
+ break;
+ }
+ default:
+ return NPERR_GENERIC_ERROR;
+ }
+
+ // Allocate this using the NPAPI allocator. The plugin will call
+ // NPN_Free to free this.
+ *value = static_cast<char*>(NPN_MemAlloc(result.length() + 1));
+ base::strlcpy(*value, result.c_str(), result.length() + 1);
+ *len = result.length();
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPN_SetValueForURL(NPP id,
+ NPNURLVariable variable,
+ const char* url,
+ const char* value,
+ uint32_t len) {
+ if (!id)
+ return NPERR_INVALID_PARAM;
+
+ if (!url || !*url)
+ return NPERR_INVALID_URL;
+
+ switch (variable) {
+ case NPNURLVCookie: {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (!plugin)
+ return NPERR_GENERIC_ERROR;
+
+ webkit_glue::WebPlugin* webplugin = plugin->webplugin();
+ if (!webplugin)
+ return NPERR_GENERIC_ERROR;
+
+ std::string cookie(value, len);
+ GURL cookies_url((std::string(url)));
+ webplugin->SetCookie(cookies_url, cookies_url, cookie);
+ return NPERR_NO_ERROR;
+ }
+ case NPNURLVProxy:
+ // We don't support setting proxy values, fall through...
+ break;
+ default:
+ // Fall through and return an error...
+ break;
+ }
+
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError NPN_GetAuthenticationInfo(NPP id,
+ const char* protocol,
+ const char* host,
+ int32_t port,
+ const char* scheme,
+ const char* realm,
+ char** username,
+ uint32_t* ulen,
+ char** password,
+ uint32_t* plen) {
+ if (!id || !protocol || !host || !scheme || !realm || !username ||
+ !ulen || !password || !plen)
+ return NPERR_INVALID_PARAM;
+
+ // TODO: implement me (bug 23928)
+ return NPERR_GENERIC_ERROR;
+}
+
+uint32_t NPN_ScheduleTimer(NPP id,
+ uint32_t interval,
+ NPBool repeat,
+ void (*func)(NPP id, uint32_t timer_id)) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (!plugin)
+ return 0;
+
+ return plugin->ScheduleTimer(interval, repeat, func);
+}
+
+void NPN_UnscheduleTimer(NPP id, uint32_t timer_id) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin)
+ plugin->UnscheduleTimer(timer_id);
+}
+
+NPError NPN_PopUpContextMenu(NPP id, NPMenu* menu) {
+ if (!menu)
+ return NPERR_INVALID_PARAM;
+
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin.get()) {
+ return plugin->PopUpContextMenu(menu);
+ }
+ NOTREACHED();
+ return NPERR_GENERIC_ERROR;
+}
+
+NPBool NPN_ConvertPoint(NPP id, double sourceX, double sourceY,
+ NPCoordinateSpace sourceSpace,
+ double *destX, double *destY,
+ NPCoordinateSpace destSpace) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(id));
+ if (plugin.get()) {
+ return plugin->ConvertPoint(sourceX, sourceY, sourceSpace,
+ destX, destY, destSpace);
+ }
+ NOTREACHED();
+ return false;
+}
+
+NPBool NPN_HandleEvent(NPP id, void *event, NPBool handled) {
+ // TODO: Implement advanced key handling: http://crbug.com/46578
+ NOTIMPLEMENTED();
+ return false;
+}
+
+NPBool NPN_UnfocusInstance(NPP id, NPFocusDirection direction) {
+ // TODO: Implement advanced key handling: http://crbug.com/46578
+ NOTIMPLEMENTED();
+ return false;
+}
+
+void NPN_URLRedirectResponse(NPP instance, void* notify_data, NPBool allow) {
+ scoped_refptr<NPAPI::PluginInstance> plugin(FindInstance(instance));
+ if (plugin.get()) {
+ plugin->URLRedirectResponse(!!allow, notify_data);
+ }
+}
+
+} // extern "C"
diff --git a/webkit/glue/plugins/plugin_host.h b/webkit/glue/plugins/plugin_host.h
new file mode 100644
index 0000000..4763df1
--- /dev/null
+++ b/webkit/glue/plugins/plugin_host.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO: Need mechanism to cleanup the static instance
+
+#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_HOST_H__
+#define WEBKIT_GLUE_PLUGIN_PLUGIN_HOST_H__
+
+#include <string>
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "third_party/npapi/bindings/npapi.h"
+#include "third_party/npapi/bindings/nphostapi.h"
+
+namespace NPAPI
+{
+class PluginInstance;
+
+// The Plugin Host implements the NPN_xxx functions for NPAPI plugins.
+// These are the functions exposed from the Plugin Host for use
+// by the Plugin.
+//
+// The PluginHost is managed as a singleton. This isn't strictly
+// necessary, but since the callback functions are all global C
+// functions, there is really no point in having per-instance PluginHosts.
+class PluginHost : public base::RefCounted<PluginHost> {
+ public:
+ // Access the single PluginHost instance. Callers
+ // must call deref() when finished with the object.
+ static PluginHost *Singleton();
+
+ // The table of functions provided to the plugin.
+ NPNetscapeFuncs *host_functions() { return &host_funcs_; }
+
+ // Helper function for parsing post headers, and applying attributes
+ // to the stream. NPAPI post data include headers + data combined.
+ // This function parses it out and adds it to the stream in a WebKit
+ // style.
+ static bool SetPostData(const char *buf,
+ uint32 length,
+ std::vector<std::string>* names,
+ std::vector<std::string>* values,
+ std::vector<char>* body);
+
+ void PatchNPNetscapeFuncs(NPNetscapeFuncs* overrides);
+
+ private:
+ friend class base::RefCounted<PluginHost>;
+
+ virtual ~PluginHost();
+
+ PluginHost();
+ void InitializeHostFuncs();
+ static scoped_refptr<PluginHost> singleton_;
+ NPNetscapeFuncs host_funcs_;
+ DISALLOW_COPY_AND_ASSIGN(PluginHost);
+};
+
+} // namespace NPAPI
+
+#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_HOST_H__
diff --git a/webkit/glue/plugins/plugin_instance.cc b/webkit/glue/plugins/plugin_instance.cc
new file mode 100644
index 0000000..4ccbadf
--- /dev/null
+++ b/webkit/glue/plugins/plugin_instance.cc
@@ -0,0 +1,680 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+
+#include "webkit/glue/plugins/plugin_instance.h"
+
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/glue/plugins/plugin_host.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+#include "webkit/glue/plugins/plugin_stream_url.h"
+#include "webkit/glue/plugins/plugin_string_stream.h"
+#include "webkit/glue/plugins/webplugin.h"
+#include "webkit/glue/plugins/webplugin_delegate.h"
+#include "net/base/escape.h"
+
+#if defined(OS_MACOSX)
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+namespace NPAPI {
+
+PluginInstance::PluginInstance(PluginLib *plugin, const std::string &mime_type)
+ : plugin_(plugin),
+ npp_(0),
+ host_(PluginHost::Singleton()),
+ npp_functions_(plugin->functions()),
+ window_handle_(0),
+ windowless_(false),
+ transparent_(true),
+ webplugin_(0),
+ mime_type_(mime_type),
+ use_mozilla_user_agent_(false),
+#if defined (OS_MACOSX)
+#ifdef NP_NO_QUICKDRAW
+ drawing_model_(NPDrawingModelCoreGraphics),
+#else
+ drawing_model_(NPDrawingModelQuickDraw),
+#endif
+#ifdef NP_NO_CARBON
+ event_model_(NPEventModelCocoa),
+#else
+ event_model_(NPEventModelCarbon),
+#endif
+ currently_handled_event_(NULL),
+#endif
+ message_loop_(MessageLoop::current()),
+ load_manually_(false),
+ in_close_streams_(false),
+ next_timer_id_(1),
+ next_notify_id_(0),
+ next_range_request_id_(0),
+ handles_url_redirects_(false) {
+ npp_ = new NPP_t();
+ npp_->ndata = 0;
+ npp_->pdata = 0;
+
+ memset(&zero_padding_, 0, sizeof(zero_padding_));
+ DCHECK(message_loop_);
+}
+
+PluginInstance::~PluginInstance() {
+ CloseStreams();
+
+ if (npp_ != 0) {
+ delete npp_;
+ npp_ = 0;
+ }
+
+ if (plugin_)
+ plugin_->CloseInstance();
+}
+
+PluginStreamUrl* PluginInstance::CreateStream(unsigned long resource_id,
+ const GURL& url,
+ const std::string& mime_type,
+ int notify_id) {
+
+ bool notify;
+ void* notify_data;
+ GetNotifyData(notify_id, &notify, &notify_data);
+ PluginStreamUrl* stream = new PluginStreamUrl(
+ resource_id, url, this, notify, notify_data);
+
+ AddStream(stream);
+ return stream;
+}
+
+void PluginInstance::AddStream(PluginStream* stream) {
+ open_streams_.push_back(make_scoped_refptr(stream));
+}
+
+void PluginInstance::RemoveStream(PluginStream* stream) {
+ if (in_close_streams_)
+ return;
+
+ std::vector<scoped_refptr<PluginStream> >::iterator stream_index;
+ for (stream_index = open_streams_.begin();
+ stream_index != open_streams_.end(); ++stream_index) {
+ if (*stream_index == stream) {
+ open_streams_.erase(stream_index);
+ break;
+ }
+ }
+}
+
+bool PluginInstance::IsValidStream(const NPStream* stream) {
+ std::vector<scoped_refptr<PluginStream> >::iterator stream_index;
+ for (stream_index = open_streams_.begin();
+ stream_index != open_streams_.end(); ++stream_index) {
+ if ((*stream_index)->stream() == stream)
+ return true;
+ }
+
+ return false;
+}
+
+void PluginInstance::CloseStreams() {
+ in_close_streams_ = true;
+ for (unsigned int index = 0; index < open_streams_.size(); ++index) {
+ // Close all streams on the way down.
+ open_streams_[index]->Close(NPRES_USER_BREAK);
+ }
+ open_streams_.clear();
+ in_close_streams_ = false;
+}
+
+webkit_glue::WebPluginResourceClient* PluginInstance::GetRangeRequest(
+ int id) {
+ PendingRangeRequestMap::iterator iter = pending_range_requests_.find(id);
+ if (iter == pending_range_requests_.end()) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ webkit_glue::WebPluginResourceClient* rv = iter->second->AsResourceClient();
+ pending_range_requests_.erase(iter);
+ return rv;
+}
+
+bool PluginInstance::Start(const GURL& url,
+ char** const param_names,
+ char** const param_values,
+ int param_count,
+ bool load_manually) {
+ load_manually_ = load_manually;
+ unsigned short mode = load_manually_ ? NP_FULL : NP_EMBED;
+ npp_->ndata = this;
+
+ NPError err = NPP_New(mode, param_count,
+ const_cast<char **>(param_names), const_cast<char **>(param_values));
+
+ if (err == NPERR_NO_ERROR) {
+ handles_url_redirects_ =
+ ((npp_functions_->version >= NPVERS_HAS_URL_REDIRECT_HANDLING) &&
+ (npp_functions_->urlredirectnotify));
+ }
+ return err == NPERR_NO_ERROR;
+}
+
+NPObject *PluginInstance::GetPluginScriptableObject() {
+ NPObject *value = NULL;
+ NPError error = NPP_GetValue(NPPVpluginScriptableNPObject, &value);
+ if (error != NPERR_NO_ERROR || value == NULL)
+ return NULL;
+ return value;
+}
+
+// WebPluginLoadDelegate methods
+void PluginInstance::DidFinishLoadWithReason(
+ const GURL& url, NPReason reason, int notify_id) {
+ bool notify;
+ void* notify_data;
+ GetNotifyData(notify_id, &notify, &notify_data);
+ if (!notify) {
+ NOTREACHED();
+ return;
+ }
+
+ NPP_URLNotify(url.spec().c_str(), reason, notify_data);
+}
+
+unsigned PluginInstance::GetBackingTextureId() {
+ // By default the plugin instance is not backed by an OpenGL texture.
+ return 0;
+}
+
+// NPAPI methods
+NPError PluginInstance::NPP_New(unsigned short mode,
+ short argc,
+ char *argn[],
+ char *argv[]) {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->newp != 0);
+ DCHECK(argc >= 0);
+
+ if (npp_functions_->newp != 0) {
+ return npp_functions_->newp(
+ (NPMIMEType)mime_type_.c_str(), npp_, mode, argc, argn, argv, NULL);
+ }
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+}
+
+void PluginInstance::NPP_Destroy() {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->destroy != 0);
+
+ if (npp_functions_->destroy != 0) {
+ NPSavedData *savedData = 0;
+ npp_functions_->destroy(npp_, &savedData);
+
+ // TODO: Support savedData. Technically, these need to be
+ // saved on a per-URL basis, and then only passed
+ // to new instances of the plugin at the same URL.
+ // Sounds like a huge security risk. When we do support
+ // these, we should pass them back to the PluginLib
+ // to be stored there.
+ DCHECK(savedData == 0);
+ }
+
+ for (unsigned int file_index = 0; file_index < files_created_.size();
+ file_index++) {
+ file_util::Delete(files_created_[file_index], false);
+ }
+
+ // Ensure that no timer callbacks are invoked after NPP_Destroy.
+ timers_.clear();
+}
+
+NPError PluginInstance::NPP_SetWindow(NPWindow *window) {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->setwindow != 0);
+
+ if (npp_functions_->setwindow != 0) {
+ return npp_functions_->setwindow(npp_, window);
+ }
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+}
+
+NPError PluginInstance::NPP_NewStream(NPMIMEType type,
+ NPStream *stream,
+ NPBool seekable,
+ unsigned short *stype) {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->newstream != 0);
+ if (npp_functions_->newstream != 0) {
+ return npp_functions_->newstream(npp_, type, stream, seekable, stype);
+ }
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+}
+
+NPError PluginInstance::NPP_DestroyStream(NPStream *stream, NPReason reason) {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->destroystream != 0);
+
+ if (stream == NULL || !IsValidStream(stream) || (stream->ndata == NULL))
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ if (npp_functions_->destroystream != 0) {
+ NPError result = npp_functions_->destroystream(npp_, stream, reason);
+ stream->ndata = NULL;
+ return result;
+ }
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+}
+
+int PluginInstance::NPP_WriteReady(NPStream *stream) {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->writeready != 0);
+ if (npp_functions_->writeready != 0) {
+ return npp_functions_->writeready(npp_, stream);
+ }
+ return 0;
+}
+
+int PluginInstance::NPP_Write(NPStream *stream,
+ int offset,
+ int len,
+ void *buffer) {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->write != 0);
+ if (npp_functions_->write != 0) {
+ return npp_functions_->write(npp_, stream, offset, len, buffer);
+ }
+ return 0;
+}
+
+void PluginInstance::NPP_StreamAsFile(NPStream *stream, const char *fname) {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->asfile != 0);
+ if (npp_functions_->asfile != 0) {
+ npp_functions_->asfile(npp_, stream, fname);
+ }
+
+ // Creating a temporary FilePath instance on the stack as the explicit
+ // FilePath constructor with StringType as an argument causes a compiler
+ // error when invoked via vector push back.
+ FilePath file_name = FilePath::FromWStringHack(UTF8ToWide(fname));
+ files_created_.push_back(file_name);
+}
+
+void PluginInstance::NPP_URLNotify(const char *url,
+ NPReason reason,
+ void *notifyData) {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->urlnotify != 0);
+ if (npp_functions_->urlnotify != 0) {
+ npp_functions_->urlnotify(npp_, url, reason, notifyData);
+ }
+}
+
+NPError PluginInstance::NPP_GetValue(NPPVariable variable, void *value) {
+ DCHECK(npp_functions_ != 0);
+ // getvalue is NULL for Shockwave
+ if (npp_functions_->getvalue != 0) {
+ return npp_functions_->getvalue(npp_, variable, value);
+ }
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+}
+
+NPError PluginInstance::NPP_SetValue(NPNVariable variable, void *value) {
+ DCHECK(npp_functions_ != 0);
+ if (npp_functions_->setvalue != 0) {
+ return npp_functions_->setvalue(npp_, variable, value);
+ }
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+}
+
+short PluginInstance::NPP_HandleEvent(void* event) {
+ DCHECK(npp_functions_ != 0);
+ DCHECK(npp_functions_->event != 0);
+ if (npp_functions_->event != 0) {
+ return npp_functions_->event(npp_, (void*)event);
+ }
+ return false;
+}
+
+bool PluginInstance::NPP_Print(NPPrint* platform_print) {
+ DCHECK(npp_functions_ != 0);
+ if (npp_functions_->print != 0) {
+ npp_functions_->print(npp_, platform_print);
+ return true;
+ }
+ return false;
+}
+
+NPError PluginInstance::NPP_ClearSiteData(uint64 flags,
+ const char* domain,
+ uint64 max_age) {
+ DCHECK(npp_functions_ != 0);
+ // TODO(bauerb): Call NPAPI function when it is defined in the header.
+ return NPERR_NO_ERROR;
+}
+
+void PluginInstance::NPP_URLRedirectNotify(const char* url, int32_t status,
+ void* notify_data) {
+ DCHECK(npp_functions_ != 0);
+ if (npp_functions_->urlredirectnotify != 0) {
+ npp_functions_->urlredirectnotify(npp_, url, status, notify_data);
+ }
+}
+
+void PluginInstance::SendJavaScriptStream(const GURL& url,
+ const std::string& result,
+ bool success,
+ int notify_id) {
+ bool notify;
+ void* notify_data;
+ GetNotifyData(notify_id, &notify, &notify_data);
+
+ if (success) {
+ PluginStringStream *stream =
+ new PluginStringStream(this, url, notify, notify_data);
+ AddStream(stream);
+ stream->SendToPlugin(result, "text/html");
+ } else {
+ // NOTE: Sending an empty stream here will crash MacroMedia
+ // Flash 9. Just send the URL Notify.
+ if (notify)
+ NPP_URLNotify(url.spec().c_str(), NPRES_DONE, notify_data);
+ }
+}
+
+void PluginInstance::DidReceiveManualResponse(const GURL& url,
+ const std::string& mime_type,
+ const std::string& headers,
+ uint32 expected_length,
+ uint32 last_modified) {
+ DCHECK(load_manually_);
+
+ plugin_data_stream_ = CreateStream(-1, url, mime_type, 0);
+ plugin_data_stream_->DidReceiveResponse(mime_type, headers, expected_length,
+ last_modified, true);
+}
+
+void PluginInstance::DidReceiveManualData(const char* buffer, int length) {
+ DCHECK(load_manually_);
+ if (plugin_data_stream_.get() != NULL) {
+ plugin_data_stream_->DidReceiveData(buffer, length, 0);
+ }
+}
+
+void PluginInstance::DidFinishManualLoading() {
+ DCHECK(load_manually_);
+ if (plugin_data_stream_.get() != NULL) {
+ plugin_data_stream_->DidFinishLoading();
+ plugin_data_stream_->Close(NPRES_DONE);
+ plugin_data_stream_ = NULL;
+ }
+}
+
+void PluginInstance::DidManualLoadFail() {
+ DCHECK(load_manually_);
+ if (plugin_data_stream_.get() != NULL) {
+ plugin_data_stream_->DidFail();
+ plugin_data_stream_ = NULL;
+ }
+}
+
+void PluginInstance::PluginThreadAsyncCall(void (*func)(void *),
+ void *user_data) {
+ message_loop_->PostTask(
+ FROM_HERE, NewRunnableMethod(
+ this, &PluginInstance::OnPluginThreadAsyncCall, func, user_data));
+}
+
+void PluginInstance::OnPluginThreadAsyncCall(void (*func)(void *),
+ void *user_data) {
+ // Do not invoke the callback if NPP_Destroy has already been invoked.
+ if (webplugin_)
+ func(user_data);
+}
+
+uint32 PluginInstance::ScheduleTimer(uint32 interval,
+ NPBool repeat,
+ void (*func)(NPP id, uint32 timer_id)) {
+ // Use next timer id.
+ uint32 timer_id;
+ timer_id = next_timer_id_;
+ ++next_timer_id_;
+ DCHECK(next_timer_id_ != 0);
+
+ // Record timer interval and repeat.
+ TimerInfo info;
+ info.interval = interval;
+ info.repeat = repeat ? true : false;
+ timers_[timer_id] = info;
+
+ // Schedule the callback.
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ NewRunnableMethod(
+ this, &PluginInstance::OnTimerCall, func, npp_, timer_id),
+ interval);
+ return timer_id;
+}
+
+void PluginInstance::UnscheduleTimer(uint32 timer_id) {
+ // Remove info about the timer.
+ TimerMap::iterator it = timers_.find(timer_id);
+ if (it != timers_.end())
+ timers_.erase(it);
+}
+
+#if !defined(OS_MACOSX)
+NPError PluginInstance::PopUpContextMenu(NPMenu* menu) {
+ NOTIMPLEMENTED();
+ return NPERR_GENERIC_ERROR;
+}
+#endif
+
+void PluginInstance::OnTimerCall(void (*func)(NPP id, uint32 timer_id),
+ NPP id,
+ uint32 timer_id) {
+ // Do not invoke callback if the timer has been unscheduled.
+ TimerMap::iterator it = timers_.find(timer_id);
+ if (it == timers_.end())
+ return;
+
+ // Get all information about the timer before invoking the callback. The
+ // callback might unschedule the timer.
+ TimerInfo info = it->second;
+
+ func(id, timer_id);
+
+ // If the timer was unscheduled by the callback, just free up the timer id.
+ if (timers_.find(timer_id) == timers_.end())
+ return;
+
+ // Reschedule repeating timers after invoking the callback so callback is not
+ // re-entered if it pumps the messager loop.
+ if (info.repeat) {
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ NewRunnableMethod(
+ this, &PluginInstance::OnTimerCall, func, npp_, timer_id),
+ info.interval);
+ } else {
+ timers_.erase(it);
+ }
+}
+
+void PluginInstance::PushPopupsEnabledState(bool enabled) {
+ popups_enabled_stack_.push(enabled);
+}
+
+void PluginInstance::PopPopupsEnabledState() {
+ popups_enabled_stack_.pop();
+}
+
+void PluginInstance::RequestRead(NPStream* stream, NPByteRange* range_list) {
+ std::string range_info = "bytes=";
+
+ while (range_list) {
+ range_info += base::IntToString(range_list->offset);
+ range_info.push_back('-');
+ range_info +=
+ base::IntToString(range_list->offset + range_list->length - 1);
+ range_list = range_list->next;
+ if (range_list)
+ range_info.push_back(',');
+ }
+
+ if (plugin_data_stream_) {
+ if (plugin_data_stream_->stream() == stream) {
+ webplugin_->CancelDocumentLoad();
+ plugin_data_stream_ = NULL;
+ }
+ }
+
+ // The lifetime of a NPStream instance depends on the PluginStream instance
+ // which owns it. When a plugin invokes NPN_RequestRead on a seekable stream,
+ // we don't want to create a new stream when the corresponding response is
+ // received. We send over a cookie which represents the PluginStream
+ // instance which is sent back from the renderer when the response is
+ // received.
+ std::vector<scoped_refptr<PluginStream> >::iterator stream_index;
+ for (stream_index = open_streams_.begin();
+ stream_index != open_streams_.end(); ++stream_index) {
+ PluginStream* plugin_stream = *stream_index;
+ if (plugin_stream->stream() == stream) {
+ // A stream becomes seekable the first time NPN_RequestRead
+ // is called on it.
+ plugin_stream->set_seekable(true);
+
+ pending_range_requests_[++next_range_request_id_] = plugin_stream;
+ webplugin_->InitiateHTTPRangeRequest(
+ stream->url, range_info.c_str(), next_range_request_id_);
+ return;
+ }
+ }
+ NOTREACHED();
+}
+
+void PluginInstance::RequestURL(const char* url,
+ const char* method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ bool notify,
+ void* notify_data) {
+ int notify_id = 0;
+ if (notify) {
+ notify_id = ++next_notify_id_;
+ pending_requests_[notify_id] = notify_data;
+ }
+
+ webplugin_->HandleURLRequest(
+ url, method, target, buf, len, notify_id, popups_allowed(),
+ notify ? handles_url_redirects_ : false);
+}
+
+bool PluginInstance::ConvertPoint(double source_x, double source_y,
+ NPCoordinateSpace source_space,
+ double* dest_x, double* dest_y,
+ NPCoordinateSpace dest_space) {
+#if defined(OS_MACOSX)
+ CGRect main_display_bounds = CGDisplayBounds(CGMainDisplayID());
+
+ double flipped_screen_x = source_x;
+ double flipped_screen_y = source_y;
+ switch(source_space) {
+ case NPCoordinateSpacePlugin:
+ flipped_screen_x += plugin_origin_.x();
+ flipped_screen_y += plugin_origin_.y();
+ break;
+ case NPCoordinateSpaceWindow:
+ flipped_screen_x += containing_window_frame_.x();
+ flipped_screen_y = containing_window_frame_.height() - source_y +
+ containing_window_frame_.y();
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ flipped_screen_x += containing_window_frame_.x();
+ flipped_screen_y += containing_window_frame_.y();
+ break;
+ case NPCoordinateSpaceScreen:
+ flipped_screen_y = main_display_bounds.size.height - flipped_screen_y;
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ break;
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ double target_x = flipped_screen_x;
+ double target_y = flipped_screen_y;
+ switch(dest_space) {
+ case NPCoordinateSpacePlugin:
+ target_x -= plugin_origin_.x();
+ target_y -= plugin_origin_.y();
+ break;
+ case NPCoordinateSpaceWindow:
+ target_x -= containing_window_frame_.x();
+ target_y -= containing_window_frame_.y();
+ target_y = containing_window_frame_.height() - target_y;
+ break;
+ case NPCoordinateSpaceFlippedWindow:
+ target_x -= containing_window_frame_.x();
+ target_y -= containing_window_frame_.y();
+ break;
+ case NPCoordinateSpaceScreen:
+ target_y = main_display_bounds.size.height - flipped_screen_y;
+ break;
+ case NPCoordinateSpaceFlippedScreen:
+ break;
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ if (dest_x)
+ *dest_x = target_x;
+ if (dest_y)
+ *dest_y = target_y;
+ return true;
+#else
+ NOTIMPLEMENTED();
+ return false;
+#endif
+}
+
+void PluginInstance::GetNotifyData(
+ int notify_id, bool* notify, void** notify_data) {
+ PendingRequestMap::iterator iter = pending_requests_.find(notify_id);
+ if (iter != pending_requests_.end()) {
+ *notify = true;
+ *notify_data = iter->second;
+ pending_requests_.erase(iter);
+ } else {
+ *notify = false;
+ *notify_data = NULL;
+ }
+}
+
+void PluginInstance::URLRedirectResponse(bool allow, void* notify_data) {
+ // The notify_data passed in allows us to identify the matching stream.
+ std::vector<scoped_refptr<PluginStream> >::iterator stream_index;
+ for (stream_index = open_streams_.begin();
+ stream_index != open_streams_.end(); ++stream_index) {
+ PluginStream* plugin_stream = *stream_index;
+ if (plugin_stream->notify_data() == notify_data) {
+ webkit_glue::WebPluginResourceClient* resource_client =
+ plugin_stream->AsResourceClient();
+ webplugin_->URLRedirectResponse(allow, resource_client->ResourceId());
+ if (allow) {
+ plugin_stream->UpdateUrl(
+ plugin_stream->pending_redirect_url().c_str());
+ }
+ break;
+ }
+ }
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_instance.h b/webkit/glue/plugins/plugin_instance.h
new file mode 100644
index 0000000..fa0320e
--- /dev/null
+++ b/webkit/glue/plugins/plugin_instance.h
@@ -0,0 +1,375 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO: Need to deal with NPAPI's NPSavedData.
+// I haven't seen plugins use it yet.
+
+#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_INSTANCE_H__
+#define WEBKIT_GLUE_PLUGIN_PLUGIN_INSTANCE_H__
+
+#include <map>
+#include <stack>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "gfx/native_widget_types.h"
+#include "gfx/point.h"
+#include "gfx/rect.h"
+#include "googleurl/src/gurl.h"
+#include "third_party/npapi/bindings/npapi.h"
+#include "third_party/npapi/bindings/nphostapi.h"
+
+class MessageLoop;
+
+namespace webkit_glue {
+class WebPlugin;
+class WebPluginResourceClient;
+}
+
+namespace NPAPI
+{
+class PluginLib;
+class PluginHost;
+class PluginStream;
+class PluginStreamUrl;
+class PluginDataStream;
+#if defined(OS_MACOSX)
+class ScopedCurrentPluginEvent;
+#endif
+
+// A PluginInstance is an active, running instance of a Plugin.
+// A single plugin may have many PluginInstances.
+class PluginInstance : public base::RefCountedThreadSafe<PluginInstance> {
+ public:
+ // Create a new instance of a plugin. The PluginInstance
+ // will hold a reference to the plugin.
+ PluginInstance(PluginLib *plugin, const std::string &mime_type);
+
+ // Activates the instance by calling NPP_New.
+ // This should be called after our instance is all
+ // setup from the host side and we are ready to receive
+ // requests from the plugin. We must not call any
+ // functions on the plugin instance until start has
+ // been called.
+ //
+ // url: The instance URL.
+ // param_names: the list of names of attributes passed via the
+ // element.
+ // param_values: the list of values corresponding to param_names
+ // param_count: number of attributes
+ // load_manually: if true indicates that the plugin data would be passed
+ // from webkit. if false indicates that the plugin should
+ // download the data.
+ // This also controls whether the plugin is instantiated as
+ // a full page plugin (NP_FULL) or embedded (NP_EMBED)
+ //
+ bool Start(const GURL& url,
+ char** const param_names,
+ char** const param_values,
+ int param_count,
+ bool load_manually);
+
+ // NPAPI's instance identifier for this instance
+ NPP npp() { return npp_; }
+
+ // Get/Set for the instance's window handle.
+ gfx::PluginWindowHandle window_handle() const { return window_handle_; }
+ void set_window_handle(gfx::PluginWindowHandle value) {
+ window_handle_ = value;
+ }
+
+ // Get/Set whether this instance is in Windowless mode.
+ // Default is false.
+ bool windowless() { return windowless_; }
+ void set_windowless(bool value) { windowless_ = value; }
+
+ // Get/Set whether this instance is transparent.
+ // This only applies to windowless plugins. Transparent
+ // plugins require that webkit paint the background.
+ // Default is true.
+ bool transparent() { return transparent_; }
+ void set_transparent(bool value) { transparent_ = value; }
+
+ // Get/Set the WebPlugin associated with this instance
+ webkit_glue::WebPlugin* webplugin() { return webplugin_; }
+ void set_web_plugin(webkit_glue::WebPlugin* webplugin) {
+ webplugin_ = webplugin;
+ }
+
+ // Get the mimeType for this plugin stream
+ const std::string &mime_type() { return mime_type_; }
+
+ NPAPI::PluginLib* plugin_lib() { return plugin_; }
+
+#if defined(OS_MACOSX)
+ // Get/Set the Mac NPAPI drawing and event models
+ NPDrawingModel drawing_model() { return drawing_model_; }
+ void set_drawing_model(NPDrawingModel value) { drawing_model_ = value; }
+ NPEventModel event_model() { return event_model_; }
+ void set_event_model(NPEventModel value) { event_model_ = value; }
+ // Updates the instance's tracking of the location of the plugin location
+ // relative to the upper left of the screen.
+ void set_plugin_origin(const gfx::Point& origin) { plugin_origin_ = origin; }
+ // Updates the instance's tracking of the frame of the containing window
+ // relative to the upper left of the screen.
+ void set_window_frame(const gfx::Rect& frame) {
+ containing_window_frame_ = frame;
+ }
+#endif
+
+ // Creates a stream for sending an URL. If notify_id is non-zero, it will
+ // send a notification to the plugin when the stream is complete; otherwise it
+ // will not. Set object_url to true if the load is for the object tag's url,
+ // or false if it's for a url that the plugin fetched through
+ // NPN_GetUrl[Notify].
+ PluginStreamUrl* CreateStream(unsigned long resource_id,
+ const GURL& url,
+ const std::string& mime_type,
+ int notify_id);
+
+ // For each instance, we track all streams. When the
+ // instance closes, all remaining streams are also
+ // closed. All streams associated with this instance
+ // should call AddStream so that they can be cleaned
+ // up when the instance shuts down.
+ void AddStream(PluginStream* stream);
+
+ // This is called when a stream is closed. We remove the stream from the
+ // list, which releases the reference maintained to the stream.
+ void RemoveStream(PluginStream* stream);
+
+ // Closes all open streams on this instance.
+ void CloseStreams();
+
+ // Returns the WebPluginResourceClient object for a stream that has become
+ // seekable.
+ webkit_glue::WebPluginResourceClient* GetRangeRequest(int id);
+
+ // Have the plugin create it's script object.
+ NPObject *GetPluginScriptableObject();
+
+ // WebViewDelegate methods that we implement. This is for handling
+ // callbacks during getURLNotify.
+ void DidFinishLoadWithReason(const GURL& url, NPReason reason, int notify_id);
+
+ // If true, send the Mozilla user agent instead of Chrome's to the plugin.
+ bool use_mozilla_user_agent() { return use_mozilla_user_agent_; }
+ void set_use_mozilla_user_agent() { use_mozilla_user_agent_ = true; }
+
+ // If the plugin instance is backed by a texture, return its ID in the
+ // compositor's namespace. Otherwise return 0. Returns 0 by default.
+ virtual unsigned GetBackingTextureId();
+
+ // Helper that implements NPN_PluginThreadAsyncCall semantics
+ void PluginThreadAsyncCall(void (*func)(void *),
+ void *userData);
+
+ uint32 ScheduleTimer(uint32 interval,
+ NPBool repeat,
+ void (*func)(NPP id, uint32 timer_id));
+
+ void UnscheduleTimer(uint32 timer_id);
+
+ bool ConvertPoint(double source_x, double source_y,
+ NPCoordinateSpace source_space,
+ double* dest_x, double* dest_y,
+ NPCoordinateSpace dest_space);
+
+ NPError PopUpContextMenu(NPMenu* menu);
+
+ //
+ // NPAPI methods for calling the Plugin Instance
+ //
+ NPError NPP_New(unsigned short, short, char *[], char *[]);
+ NPError NPP_SetWindow(NPWindow *);
+ NPError NPP_NewStream(NPMIMEType, NPStream *, NPBool, unsigned short *);
+ NPError NPP_DestroyStream(NPStream *, NPReason);
+ int NPP_WriteReady(NPStream *);
+ int NPP_Write(NPStream *, int, int, void *);
+ void NPP_StreamAsFile(NPStream *, const char *);
+ void NPP_URLNotify(const char *, NPReason, void *);
+ NPError NPP_GetValue(NPPVariable, void *);
+ NPError NPP_SetValue(NPNVariable, void *);
+ short NPP_HandleEvent(void*);
+ void NPP_Destroy();
+ bool NPP_Print(NPPrint* platform_print);
+ NPError NPP_ClearSiteData(uint64, const char*, uint64);
+ void NPP_URLRedirectNotify(const char* url, int32_t status,
+ void* notify_data);
+
+ void SendJavaScriptStream(const GURL& url,
+ const std::string& result,
+ bool success,
+ int notify_id);
+
+ void DidReceiveManualResponse(const GURL& url,
+ const std::string& mime_type,
+ const std::string& headers,
+ uint32 expected_length,
+ uint32 last_modified);
+ void DidReceiveManualData(const char* buffer, int length);
+ void DidFinishManualLoading();
+ void DidManualLoadFail();
+
+ void PushPopupsEnabledState(bool enabled);
+ void PopPopupsEnabledState();
+
+ bool popups_allowed() const {
+ return popups_enabled_stack_.empty() ? false : popups_enabled_stack_.top();
+ }
+
+ // Initiates byte range reads for plugins.
+ void RequestRead(NPStream* stream, NPByteRange* range_list);
+
+ // Handles GetURL/GetURLNotify/PostURL/PostURLNotify requests initiated
+ // by plugins.
+ void RequestURL(const char* url,
+ const char* method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ bool notify,
+ void* notify_data);
+
+ // Handles NPN_URLRedirectResponse calls issued by plugins in response to
+ // HTTP URL redirect notifications.
+ void URLRedirectResponse(bool allow, void* notify_data);
+
+ bool handles_url_redirects() const { return handles_url_redirects_; }
+
+ private:
+ friend class base::RefCountedThreadSafe<PluginInstance>;
+
+#if defined(OS_MACOSX)
+ friend class ScopedCurrentPluginEvent;
+ // Sets the event that the plugin is currently handling. The object is not
+ // owned or copied, so the caller must call this again with NULL before the
+ // event pointer becomes invalid. Clients use ScopedCurrentPluginEvent rather
+ // than calling this directly.
+ void set_currently_handled_event(NPCocoaEvent* event) {
+ currently_handled_event_ = event;
+ }
+#endif
+
+ ~PluginInstance();
+ void OnPluginThreadAsyncCall(void (*func)(void *), void *userData);
+ void OnTimerCall(void (*func)(NPP id, uint32 timer_id),
+ NPP id, uint32 timer_id);
+ bool IsValidStream(const NPStream* stream);
+ void GetNotifyData(int notify_id, bool* notify, void** notify_data);
+
+ // This is a hack to get the real player plugin to work with chrome
+ // The real player plugin dll(nppl3260) when loaded by firefox is loaded via
+ // the NS COM API which is analogous to win32 COM. So the NPAPI functions in
+ // the plugin are invoked via an interface by firefox. The plugin instance
+ // handle which is passed to every NPAPI method is owned by the real player
+ // plugin, i.e. it expects the ndata member to point to a structure which
+ // it knows about. Eventually it dereferences this structure and compares
+ // a member variable at offset 0x24(Version 6.0.11.2888) /2D (Version
+ // 6.0.11.3088) with 0 and on failing this check, takes a different code
+ // path which causes a crash. Safari and Opera work with version 6.0.11.2888
+ // by chance as their ndata structure contains a 0 at the location which real
+ // player checks:(. They crash with version 6.0.11.3088 as well. The
+ // following member just adds a 96 byte padding to our PluginInstance class
+ // which is passed in the ndata member. This magic number works correctly on
+ // Vista with UAC on or off :(.
+ // NOTE: Please dont change the ordering of the member variables
+ // New members should be added after this padding array.
+ // TODO(iyengar) : Disassemble the Realplayer ndata structure and look into
+ // the possiblity of conforming to it (http://b/issue?id=936667). We
+ // could also log a bug with Real, which would save the effort.
+ uint8 zero_padding_[96];
+ scoped_refptr<NPAPI::PluginLib> plugin_;
+ NPP npp_;
+ scoped_refptr<PluginHost> host_;
+ NPPluginFuncs* npp_functions_;
+ std::vector<scoped_refptr<PluginStream> > open_streams_;
+ gfx::PluginWindowHandle window_handle_;
+ bool windowless_;
+ bool transparent_;
+ webkit_glue::WebPlugin* webplugin_;
+ std::string mime_type_;
+ GURL get_url_;
+ intptr_t get_notify_data_;
+ bool use_mozilla_user_agent_;
+#if defined(OS_MACOSX)
+ NPDrawingModel drawing_model_;
+ NPEventModel event_model_;
+ gfx::Point plugin_origin_;
+ gfx::Rect containing_window_frame_;
+ NPCocoaEvent* currently_handled_event_; // weak
+#endif
+ MessageLoop* message_loop_;
+ scoped_refptr<PluginStreamUrl> plugin_data_stream_;
+
+ // This flag if true indicates that the plugin data would be passed from
+ // webkit. if false indicates that the plugin should download the data.
+ bool load_manually_;
+
+ // Stack indicating if popups are to be enabled for the outgoing
+ // NPN_GetURL/NPN_GetURLNotify calls.
+ std::stack<bool> popups_enabled_stack_;
+
+ // True if in CloseStreams().
+ bool in_close_streams_;
+
+ // List of files created for the current plugin instance. File names are
+ // added to the list every time the NPP_StreamAsFile function is called.
+ std::vector<FilePath> files_created_;
+
+ // Next unusued timer id.
+ uint32 next_timer_id_;
+
+ // Map of timer id to settings for timer.
+ struct TimerInfo {
+ uint32 interval;
+ bool repeat;
+ };
+ typedef std::map<uint32, TimerInfo> TimerMap;
+ TimerMap timers_;
+
+ // Tracks pending GET/POST requests so that the plugin-given data doesn't
+ // cross process boundaries to an untrusted process.
+ typedef std::map<int, void*> PendingRequestMap;
+ PendingRequestMap pending_requests_;
+ int next_notify_id_;
+
+ // Used to track pending range requests so that when WebPlugin replies to us
+ // we can match the reply to the stream.
+ typedef std::map<int, scoped_refptr<PluginStream> > PendingRangeRequestMap;
+ PendingRangeRequestMap pending_range_requests_;
+ int next_range_request_id_;
+ // The plugin handles the NPAPI URL redirect notification API.
+ // See here https://wiki.mozilla.org/NPAPI:HTTPRedirectHandling
+ bool handles_url_redirects_;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginInstance);
+};
+
+#if defined(OS_MACOSX)
+// Helper to simplify correct usage of set_currently_handled_event.
+// Instantiating will set |instance|'s currently handled to |event| for the
+// lifetime of the object, then NULL when it goes out of scope.
+class ScopedCurrentPluginEvent {
+ public:
+ ScopedCurrentPluginEvent(PluginInstance* instance, NPCocoaEvent* event)
+ : instance_(instance) {
+ instance_->set_currently_handled_event(event);
+ }
+ ~ScopedCurrentPluginEvent() {
+ instance_->set_currently_handled_event(NULL);
+ }
+ private:
+ scoped_refptr<PluginInstance> instance_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedCurrentPluginEvent);
+};
+#endif
+
+} // namespace NPAPI
+
+#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_INSTANCE_H__
diff --git a/webkit/glue/plugins/plugin_instance_mac.mm b/webkit/glue/plugins/plugin_instance_mac.mm
new file mode 100644
index 0000000..9800198
--- /dev/null
+++ b/webkit/glue/plugins/plugin_instance_mac.mm
@@ -0,0 +1,133 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+
+#import <AppKit/AppKit.h>
+
+#include "base/logging.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+
+// When C++ exceptions are disabled, the C++ library defines |try| and
+// |catch| so as to allow exception-expecting C++ code to build properly when
+// language support for exceptions is not present. These macros interfere
+// with the use of |@try| and |@catch| in Objective-C files such as this one.
+// Undefine these macros here, after everything has been #included, since
+// there will be no C++ uses and only Objective-C uses from this point on.
+#undef try
+#undef catch
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5
+@interface NSMenu (SnowLeopardMenuPopUpDeclaration)
+- (BOOL)popUpMenuPositioningItem:(NSMenuItem*)item
+ atLocation:(NSPoint)location
+ inView:(NSView*)view;
+@end
+#endif
+
+namespace {
+
+// Returns an autoreleased NSEvent constructed from the given np_event,
+// targeting the given window.
+NSEvent* NSEventForNPCocoaEvent(NPCocoaEvent* np_event, NSWindow* window) {
+ bool mouse_down = 1;
+ switch (np_event->type) {
+ case NPCocoaEventMouseDown:
+ mouse_down = 1;
+ break;
+ case NPCocoaEventMouseUp:
+ mouse_down = 0;
+ break;
+ default:
+ // If plugins start bringing up context menus for things other than
+ // clicks, this will need more plumbing; for now just log it and proceed
+ // as if it were a mouse down.
+ NOTREACHED();
+ }
+ NSEventType event_type = NSLeftMouseDown;
+ switch (np_event->data.mouse.buttonNumber) {
+ case 0:
+ event_type = mouse_down ? NSLeftMouseDown : NSLeftMouseUp;
+ break;
+ case 1:
+ event_type = mouse_down ? NSRightMouseDown : NSRightMouseUp;
+ break;
+ default:
+ event_type = mouse_down ? NSOtherMouseDown : NSOtherMouseUp;
+ break;
+ }
+
+ NSInteger click_count = np_event->data.mouse.clickCount;
+ NSInteger modifiers = np_event->data.mouse.modifierFlags;
+ // NPCocoaEvent doesn't have a timestamp, so just use the current time.
+ NSEvent* event =
+ [NSEvent mouseEventWithType:event_type
+ location:NSMakePoint(0, 0)
+ modifierFlags:modifiers
+ timestamp:[[NSApp currentEvent] timestamp]
+ windowNumber:[window windowNumber]
+ context:[NSGraphicsContext currentContext]
+ eventNumber:0
+ clickCount:click_count
+ pressure:1.0];
+ return event;
+}
+
+} // namespace
+
+namespace NPAPI {
+
+NPError PluginInstance::PopUpContextMenu(NPMenu* menu) {
+ if (!currently_handled_event_)
+ return NPERR_GENERIC_ERROR;
+
+ CGRect main_display_bounds = CGDisplayBounds(CGMainDisplayID());
+ NSPoint screen_point = NSMakePoint(
+ plugin_origin_.x() + currently_handled_event_->data.mouse.pluginX,
+ plugin_origin_.y() + currently_handled_event_->data.mouse.pluginY);
+ // Plugin offsets are upper-left based, so flip vertically for Cocoa.
+ screen_point.y = main_display_bounds.size.height - screen_point.y;
+
+ NSMenu* nsmenu = reinterpret_cast<NSMenu*>(menu);
+ NPError return_val = NPERR_NO_ERROR;
+ NSWindow* window = nil;
+ @try {
+ if ([nsmenu respondsToSelector:
+ @selector(popUpMenuPositioningItem:atLocation:inView:)]) {
+ [nsmenu popUpMenuPositioningItem:nil atLocation:screen_point inView:nil];
+ } else {
+ NSRect dummy_window_rect = NSMakeRect(screen_point.x, screen_point.y,
+ 1, 1);
+ window = [[NSWindow alloc] initWithContentRect:dummy_window_rect
+ styleMask:NSBorderlessWindowMask
+ backing:NSBackingStoreNonretained
+ defer:YES];
+ [window setTitle:@"PopupMenuDummy"]; // Lets interposing identify it.
+ [window setAlphaValue:0];
+ [window makeKeyAndOrderFront:nil];
+ [NSMenu popUpContextMenu:nsmenu
+ withEvent:NSEventForNPCocoaEvent(currently_handled_event_,
+ window)
+ forView:[window contentView]];
+ }
+ }
+ @catch (NSException* e) {
+ NSLog(@"Caught exception while handling PopUpContextMenu: %@", e);
+ return_val = NPERR_GENERIC_ERROR;
+ }
+
+ if (window) {
+ @try {
+ [window orderOut:nil];
+ [window release];
+ }
+ @catch (NSException* e) {
+ NSLog(@"Caught exception while cleaning up in PopUpContextMenu: %@", e);
+ }
+ }
+
+ return return_val;
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_lib.cc b/webkit/glue/plugins/plugin_lib.cc
new file mode 100644
index 0000000..4ae4da4
--- /dev/null
+++ b/webkit/glue/plugins/plugin_lib.cc
@@ -0,0 +1,349 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_lib.h"
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/metrics/stats_counters.h"
+#include "base/string_util.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/plugin_host.h"
+#include "webkit/glue/plugins/plugin_list.h"
+
+namespace NPAPI {
+
+const char kPluginLibrariesLoadedCounter[] = "PluginLibrariesLoaded";
+const char kPluginInstancesActiveCounter[] = "PluginInstancesActive";
+
+// A list of all the instantiated plugins.
+static std::vector<scoped_refptr<PluginLib> >* g_loaded_libs;
+
+PluginLib* PluginLib::CreatePluginLib(const FilePath& filename) {
+ // We can only have one PluginLib object per plugin as it controls the per
+ // instance function calls (i.e. NP_Initialize and NP_Shutdown). So we keep
+ // a map of PluginLib objects.
+ if (!g_loaded_libs)
+ g_loaded_libs = new std::vector<scoped_refptr<PluginLib> >;
+
+ for (size_t i = 0; i < g_loaded_libs->size(); ++i) {
+ if ((*g_loaded_libs)[i]->plugin_info().path == filename)
+ return (*g_loaded_libs)[i];
+ }
+
+ WebPluginInfo info;
+ const PluginEntryPoints* entry_points = NULL;
+ if (!PluginList::Singleton()->ReadPluginInfo(filename, &info, &entry_points))
+ return NULL;
+
+ return new PluginLib(info, entry_points);
+}
+
+void PluginLib::UnloadAllPlugins() {
+ if (g_loaded_libs) {
+ // PluginLib::Unload() can remove items from the list and even delete
+ // the list when it removes the last item, so we must work with a copy
+ // of the list so that we don't get the carpet removed under our feet.
+ std::vector<scoped_refptr<PluginLib> > loaded_libs(*g_loaded_libs);
+ for (size_t i = 0; i < loaded_libs.size(); ++i)
+ loaded_libs[i]->Unload();
+
+ if (g_loaded_libs && g_loaded_libs->empty()) {
+ delete g_loaded_libs;
+ g_loaded_libs = NULL;
+ }
+ }
+}
+
+void PluginLib::ShutdownAllPlugins() {
+ if (g_loaded_libs) {
+ for (size_t i = 0; i < g_loaded_libs->size(); ++i)
+ (*g_loaded_libs)[i]->Shutdown();
+ }
+}
+
+PluginLib::PluginLib(const WebPluginInfo& info,
+ const PluginEntryPoints* entry_points)
+ : web_plugin_info_(info),
+ library_(NULL),
+ initialized_(false),
+ saved_data_(0),
+ instance_count_(0),
+ skip_unload_(false) {
+ base::StatsCounter(kPluginLibrariesLoadedCounter).Increment();
+ memset(static_cast<void*>(&plugin_funcs_), 0, sizeof(plugin_funcs_));
+ g_loaded_libs->push_back(make_scoped_refptr(this));
+
+ if (entry_points) {
+ internal_ = true;
+ entry_points_ = *entry_points;
+ } else {
+ internal_ = false;
+ // We will read the entry points from the plugin directly.
+ memset(&entry_points_, 0, sizeof(entry_points_));
+ }
+}
+
+PluginLib::~PluginLib() {
+ base::StatsCounter(kPluginLibrariesLoadedCounter).Decrement();
+ if (saved_data_ != 0) {
+ // TODO - delete the savedData object here
+ }
+}
+
+NPPluginFuncs* PluginLib::functions() {
+ return &plugin_funcs_;
+}
+
+NPError PluginLib::NP_Initialize() {
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "PluginLib::NP_Initialize(" << web_plugin_info_.path.value()
+ << "): initialized=" << initialized_;
+ if (initialized_)
+ return NPERR_NO_ERROR;
+
+ if (!Load())
+ return NPERR_MODULE_LOAD_FAILED_ERROR;
+
+ PluginHost* host = PluginHost::Singleton();
+ if (host == 0)
+ return NPERR_GENERIC_ERROR;
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ NPError rv = entry_points_.np_initialize(host->host_functions(),
+ &plugin_funcs_);
+#else
+ NPError rv = entry_points_.np_initialize(host->host_functions());
+#if defined(OS_MACOSX)
+ // On the Mac, we need to get entry points after calling np_initialize to
+ // match the behavior of other browsers.
+ if (rv == NPERR_NO_ERROR) {
+ rv = entry_points_.np_getentrypoints(&plugin_funcs_);
+ }
+#endif // OS_MACOSX
+#endif
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "PluginLib::NP_Initialize(" << web_plugin_info_.path.value()
+ << "): result=" << rv;
+ initialized_ = (rv == NPERR_NO_ERROR);
+ return rv;
+}
+
+void PluginLib::NP_Shutdown(void) {
+ DCHECK(initialized_);
+ entry_points_.np_shutdown();
+}
+
+void PluginLib::PreventLibraryUnload() {
+ skip_unload_ = true;
+}
+
+PluginInstance* PluginLib::CreateInstance(const std::string& mime_type) {
+ PluginInstance* new_instance = new PluginInstance(this, mime_type);
+ instance_count_++;
+ base::StatsCounter(kPluginInstancesActiveCounter).Increment();
+ DCHECK_NE(static_cast<PluginInstance*>(NULL), new_instance);
+ return new_instance;
+}
+
+void PluginLib::CloseInstance() {
+ base::StatsCounter(kPluginInstancesActiveCounter).Decrement();
+ instance_count_--;
+ // If a plugin is running in its own process it will get unloaded on process
+ // shutdown.
+ if ((instance_count_ == 0) && webkit_glue::IsPluginRunningInRendererProcess())
+ Unload();
+}
+
+bool PluginLib::Load() {
+ if (library_)
+ return true;
+
+ bool rv = false;
+ base::NativeLibrary library = 0;
+
+ if (!internal_) {
+#if defined(OS_WIN)
+ // This is to work around a bug in the Real player recorder plugin which
+ // intercepts LoadLibrary calls from chrome.dll and wraps NPAPI functions
+ // provided by the plugin. It crashes if the media player plugin is being
+ // loaded. Workaround is to load the dll dynamically by getting the
+ // LoadLibrary API address from kernel32.dll which bypasses the recorder
+ // plugin.
+ if (web_plugin_info_.name.find(L"Windows Media Player") !=
+ std::wstring::npos) {
+ library = base::LoadNativeLibraryDynamically(web_plugin_info_.path);
+ } else {
+ library = base::LoadNativeLibrary(web_plugin_info_.path);
+ }
+#else // OS_WIN
+ library = base::LoadNativeLibrary(web_plugin_info_.path);
+#endif // OS_WIN
+ if (library == 0) {
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Couldn't load plugin " << web_plugin_info_.path.value();
+ return rv;
+ }
+
+#if defined(OS_MACOSX)
+ // According to the WebKit source, QuickTime at least requires us to call
+ // UseResFile on the plugin resources before loading.
+ if (library->bundle_resource_ref != -1)
+ UseResFile(library->bundle_resource_ref);
+#endif
+
+ rv = true; // assume success now
+
+ entry_points_.np_initialize =
+ (NP_InitializeFunc)base::GetFunctionPointerFromNativeLibrary(library,
+ "NP_Initialize");
+ if (entry_points_.np_initialize == 0)
+ rv = false;
+
+#if defined(OS_WIN) || defined(OS_MACOSX)
+ entry_points_.np_getentrypoints =
+ (NP_GetEntryPointsFunc)base::GetFunctionPointerFromNativeLibrary(
+ library, "NP_GetEntryPoints");
+ if (entry_points_.np_getentrypoints == 0)
+ rv = false;
+#endif
+
+ entry_points_.np_shutdown =
+ (NP_ShutdownFunc)base::GetFunctionPointerFromNativeLibrary(library,
+ "NP_Shutdown");
+ if (entry_points_.np_shutdown == 0)
+ rv = false;
+ } else {
+ rv = true;
+ }
+
+ if (rv) {
+ plugin_funcs_.size = sizeof(plugin_funcs_);
+ plugin_funcs_.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+#if !defined(OS_POSIX)
+ if (entry_points_.np_getentrypoints(&plugin_funcs_) != NPERR_NO_ERROR)
+ rv = false;
+#else
+ // On Linux and Mac, we get the plugin entry points during NP_Initialize.
+#endif
+ }
+
+ if (!internal_) {
+ if (rv) {
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Plugin " << web_plugin_info_.path.value()
+ << " loaded successfully.";
+ library_ = library;
+ } else {
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Plugin " << web_plugin_info_.path.value()
+ << " failed to load, unloading.";
+ base::UnloadNativeLibrary(library);
+ }
+ }
+
+ return rv;
+}
+
+// This class implements delayed NP_Shutdown and FreeLibrary on the plugin dll.
+class FreePluginLibraryTask : public Task {
+ public:
+ FreePluginLibraryTask(const FilePath& path,
+ base::NativeLibrary library,
+ NP_ShutdownFunc shutdown_func)
+ : path_(path),
+ library_(library),
+ NP_Shutdown_(shutdown_func) {
+ }
+
+ ~FreePluginLibraryTask() {}
+
+ void Run() {
+ if (NP_Shutdown_) {
+ // Don't call NP_Shutdown if the library has been reloaded since this task
+ // was posted.
+ bool reloaded = false;
+ if (g_loaded_libs) {
+ for (size_t i = 0; i < g_loaded_libs->size(); ++i) {
+ if ((*g_loaded_libs)[i]->plugin_info().path == path_)
+ reloaded = true;
+ }
+ }
+ if (!reloaded)
+ NP_Shutdown_();
+ }
+
+ if (library_) {
+ // Always call base::UnloadNativeLibrary so that the system reference
+ // count is decremented.
+ base::UnloadNativeLibrary(library_);
+ library_ = NULL;
+ }
+ }
+
+ private:
+ FilePath path_;
+ base::NativeLibrary library_;
+ NP_ShutdownFunc NP_Shutdown_;
+ DISALLOW_COPY_AND_ASSIGN(FreePluginLibraryTask);
+};
+
+void PluginLib::Unload() {
+ if (!internal_ && library_) {
+ // In case of single process mode, a plugin can delete itself
+ // by executing a script. So delay the unloading of the library
+ // so that the plugin will have a chance to unwind.
+ bool defer_unload = webkit_glue::IsPluginRunningInRendererProcess();
+
+/* TODO(dglazkov): Revisit when re-enabling the JSC build.
+#if USE(JSC)
+ // The plugin NPAPI instances may still be around. Delay the
+ // NP_Shutdown and FreeLibrary calls at least till the next
+ // peek message.
+ defer_unload = true;
+#endif
+*/
+
+ if (defer_unload) {
+ FreePluginLibraryTask* free_library_task =
+ new FreePluginLibraryTask(web_plugin_info_.path,
+ skip_unload_ ? NULL : library_,
+ entry_points_.np_shutdown);
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Scheduling delayed unload for plugin "
+ << web_plugin_info_.path.value();
+ MessageLoop::current()->PostTask(FROM_HERE, free_library_task);
+ } else {
+ Shutdown();
+ if (!skip_unload_) {
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Unloading plugin " << web_plugin_info_.path.value();
+ base::UnloadNativeLibrary(library_);
+ }
+ }
+
+ library_ = NULL;
+ }
+
+ for (size_t i = 0; i < g_loaded_libs->size(); ++i) {
+ if ((*g_loaded_libs)[i].get() == this) {
+ g_loaded_libs->erase(g_loaded_libs->begin() + i);
+ break;
+ }
+ }
+ if (g_loaded_libs->empty()) {
+ delete g_loaded_libs;
+ g_loaded_libs = NULL;
+ }
+}
+
+void PluginLib::Shutdown() {
+ if (initialized_ && !internal_) {
+ NP_Shutdown();
+ initialized_ = false;
+ }
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_lib.h b/webkit/glue/plugins/plugin_lib.h
new file mode 100644
index 0000000..ca46e41
--- /dev/null
+++ b/webkit/glue/plugins/plugin_lib.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PLUGIN_LIB_H_
+#define WEBKIT_GLUE_PLUGINS_PLUGIN_LIB_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/native_library.h"
+#include "base/ref_counted.h"
+#include "build/build_config.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/glue/plugins/webplugin.h"
+
+class FilePath;
+struct WebPluginInfo;
+
+namespace NPAPI {
+
+class PluginInstance;
+
+// A PluginLib is a single NPAPI Plugin Library, and is the lifecycle
+// manager for new PluginInstances.
+class PluginLib : public base::RefCounted<PluginLib> {
+ public:
+ static PluginLib* CreatePluginLib(const FilePath& filename);
+
+ // Creates a WebPluginInfo structure given a plugin's path. On success
+ // returns true, with the information being put into "info".
+ // Returns false if the library couldn't be found, or if it's not a plugin.
+ static bool ReadWebPluginInfo(const FilePath& filename, WebPluginInfo* info);
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ // Parse the result of an NP_GetMIMEDescription() call.
+ // This API is only used on Unixes, and is exposed here for testing.
+ static void ParseMIMEDescription(const std::string& description,
+ std::vector<WebPluginMimeType>* mime_types);
+#endif
+
+ // Unloads all the loaded plugin libraries and cleans up the plugin map.
+ static void UnloadAllPlugins();
+
+ // Shuts down all loaded plugin instances.
+ static void ShutdownAllPlugins();
+
+ // Get the Plugin's function pointer table.
+ NPPluginFuncs* functions();
+
+ // Creates a new instance of this plugin.
+ PluginInstance* CreateInstance(const std::string& mime_type);
+
+ // Called by the instance when the instance is tearing down.
+ void CloseInstance();
+
+ // Gets information about this plugin and the mime types that it
+ // supports.
+ const WebPluginInfo& plugin_info() { return web_plugin_info_; }
+
+ bool internal() { return internal_; }
+
+ //
+ // NPAPI functions
+ //
+
+ // NPAPI method to initialize a Plugin.
+ // Initialize can be safely called multiple times
+ NPError NP_Initialize();
+
+ // NPAPI method to shutdown a Plugin.
+ void NP_Shutdown(void);
+
+ int instance_count() const { return instance_count_; }
+
+ // Prevents the library code from being unload when Unload() is called (since
+ // some plugins crash if unloaded).
+ void PreventLibraryUnload();
+
+ // protected for testability.
+ protected:
+ friend class base::RefCounted<PluginLib>;
+
+ // Creates a new PluginLib.
+ // |entry_points| is non-NULL for internal plugins.
+ PluginLib(const WebPluginInfo& info,
+ const PluginEntryPoints* entry_points);
+
+ virtual ~PluginLib();
+
+ // Attempts to load the plugin from the library.
+ // Returns true if it is a legitimate plugin, false otherwise
+ bool Load();
+
+ // Unloads the plugin library.
+ void Unload();
+
+ // Shutdown the plugin library.
+ void Shutdown();
+
+ private:
+ bool internal_; // True for plugins that are built-in into chrome binaries.
+ WebPluginInfo web_plugin_info_; // Supported mime types, description
+ base::NativeLibrary library_; // The opened library reference.
+ NPPluginFuncs plugin_funcs_; // The struct of plugin side functions.
+ bool initialized_; // Is the plugin initialized?
+ NPSavedData *saved_data_; // Persisted plugin info for NPAPI.
+ int instance_count_; // Count of plugins in use.
+ bool skip_unload_; // True if library_ should not be unloaded.
+
+ // Function pointers to entry points into the plugin.
+ PluginEntryPoints entry_points_;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginLib);
+};
+
+} // namespace NPAPI
+
+#endif // WEBKIT_GLUE_PLUGINS_PLUGIN_LIB_H_
diff --git a/webkit/glue/plugins/plugin_lib_mac.mm b/webkit/glue/plugins/plugin_lib_mac.mm
new file mode 100644
index 0000000..89444c8
--- /dev/null
+++ b/webkit/glue/plugins/plugin_lib_mac.mm
@@ -0,0 +1,348 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Carbon/Carbon.h>
+
+#include "webkit/glue/plugins/plugin_lib.h"
+
+#include "base/mac/scoped_cftyperef.h"
+#include "base/native_library.h"
+#include "base/scoped_ptr.h"
+#include "base/string_split.h"
+#include "base/string_util.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "webkit/glue/plugins/plugin_list.h"
+
+static const short kSTRTypeDefinitionResourceID = 128;
+static const short kSTRTypeDescriptionResourceID = 127;
+static const short kSTRPluginDescriptionResourceID = 126;
+
+using base::mac::ScopedCFTypeRef;
+
+namespace NPAPI {
+
+namespace {
+
+NSDictionary* GetMIMETypes(CFBundleRef bundle) {
+ NSString* mime_filename =
+ (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle,
+ CFSTR("WebPluginMIMETypesFilename"));
+
+ if (mime_filename) {
+
+ // get the file
+
+ NSString* mime_path =
+ [NSString stringWithFormat:@"%@/Library/Preferences/%@",
+ NSHomeDirectory(), mime_filename];
+ NSDictionary* mime_file_dict =
+ [NSDictionary dictionaryWithContentsOfFile:mime_path];
+
+ // is it valid?
+
+ bool valid_file = false;
+ if (mime_file_dict) {
+ NSString* l10n_name =
+ [mime_file_dict objectForKey:@"WebPluginLocalizationName"];
+ NSString* preferred_l10n = [[NSLocale currentLocale] localeIdentifier];
+ if ([l10n_name isEqualToString:preferred_l10n])
+ valid_file = true;
+ }
+
+ if (valid_file)
+ return [mime_file_dict objectForKey:@"WebPluginMIMETypes"];
+
+ // dammit, I didn't want to have to do this
+
+ typedef void (*CreateMIMETypesPrefsPtr)(void);
+ CreateMIMETypesPrefsPtr create_prefs_file =
+ (CreateMIMETypesPrefsPtr)CFBundleGetFunctionPointerForName(
+ bundle, CFSTR("BP_CreatePluginMIMETypesPreferences"));
+ if (!create_prefs_file)
+ return nil;
+ create_prefs_file();
+
+ // one more time
+
+ mime_file_dict = [NSDictionary dictionaryWithContentsOfFile:mime_path];
+ if (mime_file_dict)
+ return [mime_file_dict objectForKey:@"WebPluginMIMETypes"];
+ else
+ return nil;
+
+ } else {
+ return (NSDictionary*)CFBundleGetValueForInfoDictionaryKey(bundle,
+ CFSTR("WebPluginMIMETypes"));
+ }
+}
+
+bool ReadPlistPluginInfo(const FilePath& filename, CFBundleRef bundle,
+ WebPluginInfo* info) {
+ NSDictionary* mime_types = GetMIMETypes(bundle);
+ if (!mime_types)
+ return false; // no type info here; try elsewhere
+
+ for (NSString* mime_type in [mime_types allKeys]) {
+ NSDictionary* mime_dict = [mime_types objectForKey:mime_type];
+ NSString* mime_desc = [mime_dict objectForKey:@"WebPluginTypeDescription"];
+ NSArray* mime_exts = [mime_dict objectForKey:@"WebPluginExtensions"];
+
+ WebPluginMimeType mime;
+ mime.mime_type = base::SysNSStringToUTF8([mime_type lowercaseString]);
+ // Remove PDF from the list of types handled by QuickTime, since it provides
+ // a worse experience than just downloading the PDF.
+ if (mime.mime_type == "application/pdf" &&
+ StartsWithASCII(filename.BaseName().value(), "QuickTime", false)) {
+ continue;
+ }
+
+ if (mime_desc)
+ mime.description = base::SysNSStringToUTF16(mime_desc);
+ for (NSString* ext in mime_exts)
+ mime.file_extensions.push_back(
+ base::SysNSStringToUTF8([ext lowercaseString]));
+
+ info->mime_types.push_back(mime);
+ }
+
+ NSString* plugin_name =
+ (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle,
+ CFSTR("WebPluginName"));
+ NSString* plugin_vers =
+ (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle,
+ CFSTR("CFBundleShortVersionString"));
+ NSString* plugin_desc =
+ (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle,
+ CFSTR("WebPluginDescription"));
+
+ if (plugin_name)
+ info->name = base::SysNSStringToUTF16(plugin_name);
+ else
+ info->name = UTF8ToUTF16(filename.BaseName().value());
+ info->path = filename;
+ if (plugin_vers)
+ info->version = base::SysNSStringToUTF16(plugin_vers);
+ if (plugin_desc)
+ info->desc = base::SysNSStringToUTF16(plugin_desc);
+ else
+ info->desc = UTF8ToUTF16(filename.BaseName().value());
+ info->enabled = true;
+
+ return true;
+}
+
+class ScopedBundleResourceFile {
+ public:
+ ScopedBundleResourceFile(CFBundleRef bundle) : bundle_(bundle) {
+ old_ref_num_ = CurResFile();
+ bundle_ref_num_ = CFBundleOpenBundleResourceMap(bundle);
+ UseResFile(bundle_ref_num_);
+ }
+ ~ScopedBundleResourceFile() {
+ UseResFile(old_ref_num_);
+ CFBundleCloseBundleResourceMap(bundle_, bundle_ref_num_);
+ }
+
+ private:
+ CFBundleRef bundle_;
+ CFBundleRefNum bundle_ref_num_;
+ ResFileRefNum old_ref_num_;
+};
+
+bool GetSTRResource(CFBundleRef bundle, short res_id,
+ std::vector<std::string>* contents) {
+ Handle res_handle = Get1Resource('STR#', res_id);
+ if (!res_handle || !*res_handle)
+ return false;
+
+ char* pointer = *res_handle;
+ short num_strings = *(short*)pointer;
+ pointer += sizeof(short);
+ for (short i = 0; i < num_strings; ++i) {
+ // Despite being 8-bits wide, these are legacy encoded. Make a round trip.
+ ScopedCFTypeRef<CFStringRef> str(CFStringCreateWithPascalStringNoCopy(
+ kCFAllocatorDefault,
+ (unsigned char*)pointer,
+ GetApplicationTextEncoding(), // is this right?
+ kCFAllocatorNull)); // perhaps CFStringGetSystemEncoding?
+ if (!str.get())
+ return false;
+ contents->push_back(base::SysCFStringRefToUTF8(str.get()));
+ pointer += 1+*reinterpret_cast<unsigned char*>(pointer);
+ }
+
+ return true;
+}
+
+bool ReadSTRPluginInfo(const FilePath& filename, CFBundleRef bundle,
+ WebPluginInfo* info) {
+ ScopedBundleResourceFile res_file(bundle);
+
+ std::vector<std::string> type_strings;
+ if (!GetSTRResource(bundle, kSTRTypeDefinitionResourceID, &type_strings))
+ return false;
+
+ std::vector<std::string> type_descs;
+ bool have_type_descs = GetSTRResource(bundle,
+ kSTRTypeDescriptionResourceID,
+ &type_descs);
+
+ std::vector<std::string> plugin_descs;
+ bool have_plugin_descs = GetSTRResource(bundle,
+ kSTRPluginDescriptionResourceID,
+ &plugin_descs);
+
+ size_t num_types = type_strings.size()/2;
+
+ for (size_t i = 0; i < num_types; ++i) {
+ WebPluginMimeType mime;
+ mime.mime_type = StringToLowerASCII(type_strings[2*i]);
+ if (have_type_descs && i < type_descs.size())
+ mime.description = UTF8ToUTF16(type_descs[i]);
+ base::SplitString(
+ StringToLowerASCII(type_strings[2*i+1]), ',', &mime.file_extensions);
+
+ info->mime_types.push_back(mime);
+ }
+
+ NSString* plugin_vers =
+ (NSString*)CFBundleGetValueForInfoDictionaryKey(bundle,
+ CFSTR("CFBundleShortVersionString"));
+
+ if (have_plugin_descs && plugin_descs.size() > 1)
+ info->name = UTF8ToUTF16(plugin_descs[1]);
+ else
+ info->name = UTF8ToUTF16(filename.BaseName().value());
+ info->path = filename;
+ if (plugin_vers)
+ info->version = base::SysNSStringToUTF16(plugin_vers);
+ if (have_plugin_descs && plugin_descs.size() > 0)
+ info->desc = UTF8ToUTF16(plugin_descs[0]);
+ else
+ info->desc = UTF8ToUTF16(filename.BaseName().value());
+ info->enabled = true;
+
+ return true;
+}
+
+} // anonymous namespace
+
+bool PluginLib::ReadWebPluginInfo(const FilePath &filename,
+ WebPluginInfo* info) {
+ // There are two ways to get information about plugin capabilities. One is an
+ // Info.plist set of keys, documented at
+ // http://developer.apple.com/documentation/InternetWeb/Conceptual/WebKit_PluginProgTopic/Concepts/AboutPlugins.html .
+ // The other is a set of STR# resources, documented at
+ // https://developer.mozilla.org/En/Gecko_Plugin_API_Reference/Plug-in_Development_Overview .
+ //
+ // Historically, the data was maintained in the STR# resources. Apple, with
+ // the introduction of WebKit, noted the weaknesses of resources and moved the
+ // information into the Info.plist. Mozilla had always supported a
+ // NP_GetMIMEDescription() entry point for Unix plugins and also supports it
+ // on the Mac to supplement the STR# format. WebKit does not support
+ // NP_GetMIMEDescription() and neither do we. (That entry point is documented
+ // at https://developer.mozilla.org/en/NP_GetMIMEDescription .) We prefer the
+ // Info.plist format because it's more extensible and has a defined encoding,
+ // but will fall back to the STR# format of the data if it is not present in
+ // the Info.plist.
+ //
+ // The parsing of the data lives in the two functions ReadSTRPluginInfo() and
+ // ReadPlistPluginInfo(), but a summary of the formats follows.
+ //
+ // Each data type handled by a plugin has several properties:
+ // - <<type0mimetype>>
+ // - <<type0fileextension0>>..<<type0fileextensionk>>
+ // - <<type0description>>
+ //
+ // Each plugin may have any number of types defined. In addition, the plugin
+ // itself has properties:
+ // - <<plugindescription>>
+ // - <<pluginname>>
+ //
+ // For the Info.plist version, the data is formatted as follows (in text plist
+ // format):
+ // {
+ // ... the usual plist keys ...
+ // WebPluginDescription = <<plugindescription>>;
+ // WebPluginMIMETypes = {
+ // <<type0mimetype>> = {
+ // WebPluginExtensions = (
+ // <<type0fileextension0>>,
+ // ...
+ // <<type0fileextensionk>>,
+ // );
+ // WebPluginTypeDescription = <<type0description>>;
+ // };
+ // <<type1mimetype>> = { ... };
+ // ...
+ // <<typenmimetype>> = { ... };
+ // };
+ // WebPluginName = <<pluginname>>;
+ // }
+ //
+ // Alternatively (and this is undocumented), rather than a WebPluginMIMETypes
+ // key, there may be a WebPluginMIMETypesFilename key. If it is present, then
+ // it is the name of a file in the user's preferences folder in which to find
+ // the WebPluginMIMETypes key. If the key is present but the file doesn't
+ // exist, we must load the plugin and call a specific function to have the
+ // plugin create the file.
+ //
+ // If we do not find those keys in the Info.plist, we fall back to the STR#
+ // resources. In them, the data is formatted as follows:
+ // STR# 128
+ // (1) <<type0mimetype>>
+ // (2) <<type0fileextension0>>,...,<<type0fileextensionk>>
+ // (3) <<type1mimetype>>
+ // (4) <<type1fileextension0>>,...,<<type1fileextensionk>>
+ // (...)
+ // (2n+1) <<typenmimetype>>
+ // (2n+2) <<typenfileextension0>>,...,<<typenfileextensionk>>
+ // STR# 127
+ // (1) <<type0description>>
+ // (2) <<type1description>>
+ // (...)
+ // (n+1) <<typendescription>>
+ // STR# 126
+ // (1) <<plugindescription>>
+ // (2) <<pluginname>>
+ //
+ // Strictly speaking, only STR# 128 is required.
+
+ ScopedCFTypeRef<CFURLRef> bundle_url(CFURLCreateFromFileSystemRepresentation(
+ kCFAllocatorDefault, (const UInt8*)filename.value().c_str(),
+ filename.value().length(), true));
+ if (!bundle_url)
+ return false;
+ ScopedCFTypeRef<CFBundleRef> bundle(CFBundleCreate(kCFAllocatorDefault,
+ bundle_url.get()));
+ if (!bundle)
+ return false;
+
+ // preflight
+
+ OSType type = 0;
+ CFBundleGetPackageInfo(bundle.get(), &type, NULL);
+ if (type != FOUR_CHAR_CODE('BRPL'))
+ return false;
+
+ CFErrorRef error;
+ Boolean would_load = CFBundlePreflightExecutable(bundle.get(), &error);
+ if (!would_load)
+ return false;
+
+ // get the info
+
+ if (ReadPlistPluginInfo(filename, bundle.get(), info))
+ return true;
+
+ if (ReadSTRPluginInfo(filename, bundle.get(), info))
+ return true;
+
+ // ... or not
+
+ return false;
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_lib_posix.cc b/webkit/glue/plugins/plugin_lib_posix.cc
new file mode 100644
index 0000000..ac937e1
--- /dev/null
+++ b/webkit/glue/plugins/plugin_lib_posix.cc
@@ -0,0 +1,256 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_lib.h"
+
+#include <dlfcn.h>
+#if defined(OS_OPENBSD)
+#include <sys/exec_elf.h>
+#else
+#include <elf.h>
+#include <fcntl.h>
+#endif
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/eintr_wrapper.h"
+#include "base/file_util.h"
+#include "base/string_split.h"
+#include "base/string_util.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "webkit/glue/plugins/plugin_list.h"
+
+// These headers must be included in this order to make the declaration gods
+// happy.
+#include "base/third_party/nspr/prcpucfg_linux.h"
+
+namespace {
+
+using NPAPI::PluginList;
+
+// Copied from nsplugindefs.h instead of including the file since it has a bunch
+// of dependencies.
+enum nsPluginVariable {
+ nsPluginVariable_NameString = 1,
+ nsPluginVariable_DescriptionString = 2
+};
+
+// Read the ELF header and return true if it is usable on
+// the current architecture (e.g. 32-bit ELF on 32-bit build).
+// Returns false on other errors as well.
+bool ELFMatchesCurrentArchitecture(const FilePath& filename) {
+ // First make sure we can open the file and it is in fact, a regular file.
+ struct stat stat_buf;
+ // Open with O_NONBLOCK so we don't block on pipes.
+ int fd = open(filename.value().c_str(), O_RDONLY|O_NONBLOCK);
+ if (fd < 0)
+ return false;
+ bool ret = (fstat(fd, &stat_buf) >= 0 && S_ISREG(stat_buf.st_mode));
+ if (HANDLE_EINTR(close(fd)) < 0)
+ return false;
+ if (!ret)
+ return false;
+
+ const size_t kELFBufferSize = 5;
+ char buffer[kELFBufferSize];
+ if (!file_util::ReadFile(filename, buffer, kELFBufferSize))
+ return false;
+
+ if (buffer[0] != ELFMAG0 ||
+ buffer[1] != ELFMAG1 ||
+ buffer[2] != ELFMAG2 ||
+ buffer[3] != ELFMAG3) {
+ // Not an ELF file, perhaps?
+ return false;
+ }
+
+ int elf_class = buffer[EI_CLASS];
+#if defined(ARCH_CPU_32_BITS)
+ if (elf_class == ELFCLASS32)
+ return true;
+#elif defined(ARCH_CPU_64_BITS)
+ if (elf_class == ELFCLASS64)
+ return true;
+#endif
+
+ return false;
+}
+
+// This structure matches enough of nspluginwrapper's NPW_PluginInfo
+// for us to extract the real plugin path.
+struct __attribute__((packed)) NSPluginWrapperInfo {
+ char ident[32]; // NSPluginWrapper magic identifier (includes version).
+ char path[PATH_MAX]; // Path to wrapped plugin.
+};
+
+// Test a plugin for whether it's been wrapped by NSPluginWrapper, and
+// if so attempt to unwrap it. Pass in an opened plugin handle; on
+// success, |dl| and |unwrapped_path| will be filled in with the newly
+// opened plugin. On failure, params are left unmodified.
+void UnwrapNSPluginWrapper(void **dl, FilePath* unwrapped_path) {
+ NSPluginWrapperInfo* info =
+ reinterpret_cast<NSPluginWrapperInfo*>(dlsym(*dl, "NPW_Plugin"));
+ if (!info)
+ return; // Not a NSPW plugin.
+
+ // Here we could check the NSPW ident field for the versioning
+ // information, but the path field is available in all versions
+ // anyway.
+
+ // Grab the path to the wrapped plugin. Just in case the structure
+ // format changes, protect against the path not being null-terminated.
+ char* path_end = static_cast<char*>(memchr(info->path, '\0',
+ sizeof(info->path)));
+ if (!path_end)
+ path_end = info->path + sizeof(info->path);
+ FilePath path = FilePath(std::string(info->path, path_end - info->path));
+
+ if (!ELFMatchesCurrentArchitecture(path)) {
+ LOG(WARNING) << path.value() << " is nspluginwrapper wrapping a "
+ << "plugin for a different architecture; it will "
+ << "work better if you instead use a native plugin.";
+ return;
+ }
+
+ void* newdl = base::LoadNativeLibrary(path);
+ if (!newdl) {
+ // We couldn't load the unwrapped plugin for some reason, despite
+ // being able to load the wrapped one. Just use the wrapped one.
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Could not use unwrapped nspluginwrapper plugin "
+ << unwrapped_path->value() << ", using the wrapped one.";
+ return;
+ }
+
+ // Unload the wrapped plugin, and use the wrapped plugin instead.
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Using unwrapped version " << unwrapped_path->value()
+ << " of nspluginwrapper-wrapped plugin.";
+ base::UnloadNativeLibrary(*dl);
+ *dl = newdl;
+ *unwrapped_path = path;
+}
+
+} // anonymous namespace
+
+namespace NPAPI {
+
+bool PluginLib::ReadWebPluginInfo(const FilePath& filename,
+ WebPluginInfo* info) {
+ // The file to reference is:
+ // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginsDirUnix.cpp
+
+ // Skip files that aren't appropriate for our architecture.
+ if (!ELFMatchesCurrentArchitecture(filename)) {
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Skipping plugin " << filename.value()
+ << " because it doesn't match the current architecture.";
+ return false;
+ }
+
+ void* dl = base::LoadNativeLibrary(filename);
+ if (!dl) {
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "While reading plugin info, unable to load library "
+ << filename.value() << ", skipping.";
+ return false;
+ }
+
+ info->path = filename;
+ info->enabled = true;
+
+ // Attempt to swap in the wrapped plugin if this is nspluginwrapper.
+ UnwrapNSPluginWrapper(&dl, &info->path);
+
+ // See comments in plugin_lib_mac regarding this symbol.
+ typedef const char* (*NP_GetMimeDescriptionType)();
+ NP_GetMimeDescriptionType NP_GetMIMEDescription =
+ reinterpret_cast<NP_GetMimeDescriptionType>(
+ dlsym(dl, "NP_GetMIMEDescription"));
+ const char* mime_description = NULL;
+ if (NP_GetMIMEDescription)
+ mime_description = NP_GetMIMEDescription();
+
+ if (mime_description)
+ ParseMIMEDescription(mime_description, &info->mime_types);
+
+ // The plugin name and description live behind NP_GetValue calls.
+ typedef NPError (*NP_GetValueType)(void* unused,
+ nsPluginVariable variable,
+ void* value_out);
+ NP_GetValueType NP_GetValue =
+ reinterpret_cast<NP_GetValueType>(dlsym(dl, "NP_GetValue"));
+ if (NP_GetValue) {
+ const char* name = NULL;
+ NP_GetValue(NULL, nsPluginVariable_NameString, &name);
+ if (name)
+ info->name = UTF8ToUTF16(name);
+
+ const char* description = NULL;
+ NP_GetValue(NULL, nsPluginVariable_DescriptionString, &description);
+ if (description)
+ info->desc = UTF8ToUTF16(description);
+
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Got info for plugin " << filename.value()
+ << " Name = \"" << UTF16ToUTF8(info->name)
+ << "\", Description = \"" << UTF16ToUTF8(info->desc) << "\".";
+ } else {
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Plugin " << filename.value()
+ << " has no GetValue() and probably won't work.";
+ }
+
+ // Intentionally not unloading the plugin here, it can lead to crashes.
+
+ return true;
+}
+
+// static
+void PluginLib::ParseMIMEDescription(
+ const std::string& description,
+ std::vector<WebPluginMimeType>* mime_types) {
+ // We parse the description here into WebPluginMimeType structures.
+ // Naively from the NPAPI docs you'd think you could use
+ // string-splitting, but the Firefox parser turns out to do something
+ // different: find the first colon, then the second, then a semi.
+ //
+ // See ParsePluginMimeDescription near
+ // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginsDirUtils.h#53
+
+ std::string::size_type ofs = 0;
+ for (;;) {
+ WebPluginMimeType mime_type;
+
+ std::string::size_type end = description.find(':', ofs);
+ if (end == std::string::npos)
+ break;
+ mime_type.mime_type = description.substr(ofs, end - ofs);
+ ofs = end + 1;
+
+ end = description.find(':', ofs);
+ if (end == std::string::npos)
+ break;
+ const std::string extensions = description.substr(ofs, end - ofs);
+ base::SplitString(extensions, ',', &mime_type.file_extensions);
+ ofs = end + 1;
+
+ end = description.find(';', ofs);
+ // It's ok for end to run off the string here. If there's no
+ // trailing semicolon we consume the remainder of the string.
+ if (end != std::string::npos) {
+ mime_type.description = UTF8ToUTF16(description.substr(ofs, end - ofs));
+ } else {
+ mime_type.description = UTF8ToUTF16(description.substr(ofs));
+ }
+ mime_types->push_back(mime_type);
+ if (end == std::string::npos)
+ break;
+ ofs = end + 1;
+ }
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_lib_unittest.cc b/webkit/glue/plugins/plugin_lib_unittest.cc
new file mode 100644
index 0000000..45c4bb6
--- /dev/null
+++ b/webkit/glue/plugins/plugin_lib_unittest.cc
@@ -0,0 +1,152 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_lib.h"
+
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Test the unloading of plugin libs. Bug http://crbug.com/46526 showed that
+// if UnloadAllPlugins() simply iterates through the g_loaded_libs global
+// variable, we can get a crash if no plugin libs were marked as always loaded.
+class PluginLibTest : public NPAPI::PluginLib {
+ public:
+ PluginLibTest() : NPAPI::PluginLib(WebPluginInfo(), NULL) {
+ }
+ using NPAPI::PluginLib::Unload;
+};
+
+TEST(PluginLibLoading, UnloadAllPlugins) {
+ // For the creation of the g_loaded_libs global variable.
+ ASSERT_EQ(static_cast<PluginLibTest*>(NULL),
+ PluginLibTest::CreatePluginLib(FilePath()));
+
+ // Try with a single plugin lib.
+ scoped_refptr<PluginLibTest> plugin_lib1(new PluginLibTest());
+ NPAPI::PluginLib::UnloadAllPlugins();
+
+ // Need to create it again, it should have been destroyed above.
+ ASSERT_EQ(static_cast<PluginLibTest*>(NULL),
+ PluginLibTest::CreatePluginLib(FilePath()));
+
+ // Try with two plugin libs.
+ plugin_lib1 = new PluginLibTest();
+ scoped_refptr<PluginLibTest> plugin_lib2(new PluginLibTest());
+ NPAPI::PluginLib::UnloadAllPlugins();
+
+ // Need to create it again, it should have been destroyed above.
+ ASSERT_EQ(static_cast<PluginLibTest*>(NULL),
+ PluginLibTest::CreatePluginLib(FilePath()));
+
+ // Now try to manually Unload one and then UnloadAll.
+ plugin_lib1 = new PluginLibTest();
+ plugin_lib2 = new PluginLibTest();
+ plugin_lib1->Unload();
+ NPAPI::PluginLib::UnloadAllPlugins();
+
+ // Need to create it again, it should have been destroyed above.
+ ASSERT_EQ(static_cast<PluginLibTest*>(NULL),
+ PluginLibTest::CreatePluginLib(FilePath()));
+
+ // Now try to manually Unload the only one and then UnloadAll.
+ plugin_lib1 = new PluginLibTest();
+ plugin_lib1->Unload();
+ NPAPI::PluginLib::UnloadAllPlugins();
+}
+
+#if defined(OS_LINUX)
+
+// Test parsing a simple description: Real Audio.
+TEST(MIMEDescriptionParse, Simple) {
+ std::vector<WebPluginMimeType> types;
+ NPAPI::PluginLib::ParseMIMEDescription(
+ "audio/x-pn-realaudio-plugin:rpm:RealAudio document;",
+ &types);
+ ASSERT_EQ(1U, types.size());
+ const WebPluginMimeType& type = types[0];
+ EXPECT_EQ("audio/x-pn-realaudio-plugin", type.mime_type);
+ ASSERT_EQ(1U, type.file_extensions.size());
+ EXPECT_EQ("rpm", type.file_extensions[0]);
+ EXPECT_EQ(ASCIIToUTF16("RealAudio document"), type.description);
+}
+
+// Test parsing a multi-entry description: QuickTime as provided by Totem.
+TEST(MIMEDescriptionParse, Multi) {
+ std::vector<WebPluginMimeType> types;
+ NPAPI::PluginLib::ParseMIMEDescription(
+ "video/quicktime:mov:QuickTime video;video/mp4:mp4:MPEG-4 "
+ "video;image/x-macpaint:pntg:MacPaint Bitmap image;image/x"
+ "-quicktime:pict, pict1, pict2:QuickTime image;video/x-m4v"
+ ":m4v:MPEG-4 video;",
+ &types);
+
+ ASSERT_EQ(5U, types.size());
+
+ // Check the x-quicktime one, since it looks tricky with spaces in the
+ // extension list.
+ const WebPluginMimeType& type = types[3];
+ EXPECT_EQ("image/x-quicktime", type.mime_type);
+ ASSERT_EQ(3U, type.file_extensions.size());
+ EXPECT_EQ("pict2", type.file_extensions[2]);
+ EXPECT_EQ(ASCIIToUTF16("QuickTime image"), type.description);
+}
+
+// Test parsing a Japanese description, since we got this wrong in the past.
+// This comes from loading Totem with LANG=ja_JP.UTF-8.
+TEST(MIMEDescriptionParse, JapaneseUTF8) {
+ std::vector<WebPluginMimeType> types;
+ NPAPI::PluginLib::ParseMIMEDescription(
+ "audio/x-ogg:ogg:Ogg \xe3\x82\xaa\xe3\x83\xbc\xe3\x83\x87"
+ "\xe3\x82\xa3\xe3\x83\xaa",
+ &types);
+
+ ASSERT_EQ(1U, types.size());
+ // Check we got the right number of Unicode characters out of the parse.
+ EXPECT_EQ(9U, types[0].description.size());
+}
+
+// Test that we handle corner cases gracefully.
+TEST(MIMEDescriptionParse, CornerCases) {
+ std::vector<WebPluginMimeType> types;
+ NPAPI::PluginLib::ParseMIMEDescription("mime/type:", &types);
+ EXPECT_TRUE(types.empty());
+
+ types.clear();
+ NPAPI::PluginLib::ParseMIMEDescription("mime/type:ext1:", &types);
+ ASSERT_EQ(1U, types.size());
+ EXPECT_EQ("mime/type", types[0].mime_type);
+ EXPECT_EQ(1U, types[0].file_extensions.size());
+ EXPECT_EQ("ext1", types[0].file_extensions[0]);
+ EXPECT_EQ(string16(), types[0].description);
+}
+
+// This Java plugin has embedded semicolons in the mime type.
+TEST(MIMEDescriptionParse, ComplicatedJava) {
+ std::vector<WebPluginMimeType> types;
+ NPAPI::PluginLib::ParseMIMEDescription(
+ "application/x-java-vm:class,jar:IcedTea;application/x-java"
+ "-applet:class,jar:IcedTea;application/x-java-applet;versio"
+ "n=1.1:class,jar:IcedTea;application/x-java-applet;version="
+ "1.1.1:class,jar:IcedTea;application/x-java-applet;version="
+ "1.1.2:class,jar:IcedTea;application/x-java-applet;version="
+ "1.1.3:class,jar:IcedTea;application/x-java-applet;version="
+ "1.2:class,jar:IcedTea;application/x-java-applet;version=1."
+ "2.1:class,jar:IcedTea;application/x-java-applet;version=1."
+ "2.2:class,jar:IcedTea;application/x-java-applet;version=1."
+ "3:class,jar:IcedTea;application/x-java-applet;version=1.3."
+ "1:class,jar:IcedTea;application/x-java-applet;version=1.4:"
+ "class,jar:IcedTea",
+ &types);
+
+ ASSERT_EQ(12U, types.size());
+ for (size_t i = 0; i < types.size(); ++i)
+ EXPECT_EQ(ASCIIToUTF16("IcedTea"), types[i].description);
+
+ // Verify that the mime types with semis are coming through ok.
+ EXPECT_TRUE(types[4].mime_type.find(';') != std::string::npos);
+}
+
+#endif // defined(OS_LINUX)
diff --git a/webkit/glue/plugins/plugin_lib_win.cc b/webkit/glue/plugins/plugin_lib_win.cc
new file mode 100644
index 0000000..382c2c8
--- /dev/null
+++ b/webkit/glue/plugins/plugin_lib_win.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_lib.h"
+
+#include "base/file_version_info.h"
+#include "base/file_version_info_win.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "webkit/glue/plugins/plugin_constants_win.h"
+#include "webkit/glue/plugins/plugin_list.h"
+
+namespace NPAPI {
+
+bool PluginLib::ReadWebPluginInfo(const FilePath &filename,
+ WebPluginInfo* info) {
+ // On windows, the way we get the mime types for the library is
+ // to check the version information in the DLL itself. This
+ // will be a string of the format: <type1>|<type2>|<type3>|...
+ // For example:
+ // video/quicktime|audio/aiff|image/jpeg
+ scoped_ptr<FileVersionInfo> version_info(
+ FileVersionInfo::CreateFileVersionInfo(filename.value()));
+ if (!version_info.get()) {
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Could not get version info for plugin "
+ << filename.value();
+ return false;
+ }
+
+ FileVersionInfoWin* version_info_win =
+ static_cast<FileVersionInfoWin*>(version_info.get());
+ PluginVersionInfo pvi;
+ pvi.mime_types = version_info_win->GetStringValue(L"MIMEType");
+ pvi.file_extensions = version_info_win->GetStringValue(L"FileExtents");
+ pvi.type_descriptions = version_info_win->GetStringValue(L"FileOpenName");
+ pvi.product_name = version_info->product_name();
+ pvi.file_description = version_info->file_description();
+ pvi.file_version = version_info->file_version();
+ pvi.path = filename;
+
+ return PluginList::CreateWebPluginInfo(pvi, info);
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_list.cc b/webkit/glue/plugins/plugin_list.cc
new file mode 100644
index 0000000..a2b4cf5
--- /dev/null
+++ b/webkit/glue/plugins/plugin_list.cc
@@ -0,0 +1,781 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_list.h"
+
+#include <algorithm>
+
+#include "base/command_line.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/string_split.h"
+#include "base/string_util.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/mime_util.h"
+#include "webkit/glue/plugins/plugin_constants_win.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/plugins/plugin_switches.h"
+
+#if defined(OS_POSIX)
+#include "base/stl_util-inl.h"
+#include "base/third_party/valgrind/valgrind.h"
+#endif // defined(OS_POSIX)
+
+namespace NPAPI {
+
+#if defined(OS_MACOSX)
+// Plugin Groups for Mac.
+// Plugins are listed here as soon as vulnerabilities and solutions
+// (new versions) are published.
+// TODO(panayiotis): Get the Real Player version on Mac, somehow.
+static const VersionRangeDefinition kQuicktimeVersionRange[] = {
+ { "", "", "7.6.6" }
+};
+static const VersionRangeDefinition kJavaVersionRange[] = {
+ { "", "", "" }
+};
+static const VersionRangeDefinition kFlashVersionRange[] = {
+ { "", "", "10.1.102" }
+};
+static const VersionRangeDefinition kSilverlightVersionRange[] = {
+ { "0", "4", "3.0.50106.0" },
+ { "4", "5", "" }
+};
+static const VersionRangeDefinition kFlip4MacVersionRange[] = {
+ { "", "", "2.2.1" }
+};
+static const VersionRangeDefinition kShockwaveVersionRange[] = {
+ { "", "", "11.5.9.615" }
+};
+static const PluginGroupDefinition kGroupDefinitions[] = {
+ { "apple-quicktime", "Quicktime", "QuickTime Plug-in", kQuicktimeVersionRange,
+ arraysize(kQuicktimeVersionRange),
+ "http://www.apple.com/quicktime/download/" },
+ { "java-runtime-environment", "Java", "Java", kJavaVersionRange,
+ arraysize(kJavaVersionRange), "http://support.apple.com/kb/HT1338" },
+ { "adobe-flash-player", "Flash", "Shockwave Flash", kFlashVersionRange,
+ arraysize(kFlashVersionRange), "http://get.adobe.com/flashplayer/" },
+ { "silverlight", "Silverlight", "Silverlight", kSilverlightVersionRange,
+ arraysize(kSilverlightVersionRange),
+ "http://www.microsoft.com/getsilverlight/" },
+ { "flip4mac", "Flip4Mac", "Flip4Mac", kFlip4MacVersionRange,
+ arraysize(kFlip4MacVersionRange),
+ "http://www.telestream.net/flip4mac-wmv/overview.htm" },
+ { "shockwave", "Shockwave", "Shockwave for Director", kShockwaveVersionRange,
+ arraysize(kShockwaveVersionRange),
+ "http://www.adobe.com/shockwave/download/" }
+};
+
+#elif defined(OS_WIN)
+// TODO(panayiotis): We should group "RealJukebox NS Plugin" with the rest of
+// the RealPlayer files.
+static const VersionRangeDefinition kQuicktimeVersionRange[] = {
+ { "", "", "7.6.9" }
+};
+static const VersionRangeDefinition kJavaVersionRange[] = {
+ { "0", "7", "6.0.220" } // "220" is not a typo.
+};
+static const VersionRangeDefinition kAdobeReaderVersionRange[] = {
+ { "10", "11", "" },
+ { "9", "10", "9.4.1" },
+ { "0", "9", "8.2.5" }
+};
+static const VersionRangeDefinition kFlashVersionRange[] = {
+ { "", "", "10.1.102" }
+};
+static const VersionRangeDefinition kSilverlightVersionRange[] = {
+ { "0", "4", "3.0.50106.0" },
+ { "4", "5", "" }
+};
+static const VersionRangeDefinition kShockwaveVersionRange[] = {
+ { "", "", "11.5.9.615" }
+};
+static const VersionRangeDefinition kDivXVersionRange[] = {
+ { "", "", "1.4.3.4" }
+};
+static const VersionRangeDefinition kRealPlayerVersionRange[] = {
+ { "", "", "12.0.1.609" }
+};
+static const PluginGroupDefinition kGroupDefinitions[] = {
+ { "apple-quicktime", "Quicktime", "QuickTime Plug-in", kQuicktimeVersionRange,
+ arraysize(kQuicktimeVersionRange),
+ "http://www.apple.com/quicktime/download/" },
+ { "java-runtime-environment", "Java 6", "Java", kJavaVersionRange,
+ arraysize(kJavaVersionRange), "http://www.java.com/" },
+ { "adobe-reader", PluginGroup::kAdobeReaderGroupName, "Adobe Acrobat",
+ kAdobeReaderVersionRange, arraysize(kAdobeReaderVersionRange),
+ "http://get.adobe.com/reader/" },
+ { "adobe-flash-player", "Flash", "Shockwave Flash", kFlashVersionRange,
+ arraysize(kFlashVersionRange), "http://get.adobe.com/flashplayer/" },
+ { "silverlight", "Silverlight", "Silverlight", kSilverlightVersionRange,
+ arraysize(kSilverlightVersionRange),
+ "http://www.microsoft.com/getsilverlight/" },
+ { "shockwave", "Shockwave", "Shockwave for Director", kShockwaveVersionRange,
+ arraysize(kShockwaveVersionRange),
+ "http://www.adobe.com/shockwave/download/" },
+ { "divx-player", "DivX Player", "DivX Web Player", kDivXVersionRange,
+ arraysize(kDivXVersionRange),
+ "http://download.divx.com/divx/autoupdate/player/"
+ "DivXWebPlayerInstaller.exe" },
+ { "realplayer", "RealPlayer", "RealPlayer", kRealPlayerVersionRange,
+ arraysize(kRealPlayerVersionRange),
+ "http://www.real.com/realplayer" },
+ // These are here for grouping, no vulnerabilities known.
+ { "windows-media-player", "Windows Media Player", "Windows Media Player",
+ NULL, 0, "" },
+ { "microsoft-office", "Microsoft Office", "Microsoft Office",
+ NULL, 0, "" },
+};
+
+#else
+static const PluginGroupDefinition kGroupDefinitions[] = {};
+#endif
+
+// static
+const PluginGroupDefinition* PluginList::GetPluginGroupDefinitions() {
+ return kGroupDefinitions;
+}
+
+// static
+size_t PluginList::GetPluginGroupDefinitionsSize() {
+ // TODO(viettrungluu): |arraysize()| doesn't work with zero-size arrays.
+ return ARRAYSIZE_UNSAFE(kGroupDefinitions);
+}
+
+base::LazyInstance<PluginList> g_singleton(base::LINKER_INITIALIZED);
+
+// static
+PluginList* PluginList::Singleton() {
+ return g_singleton.Pointer();
+}
+
+// static
+bool PluginList::DebugPluginLoading() {
+ return CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDebugPluginLoading);
+}
+
+bool PluginList::PluginsLoaded() {
+ AutoLock lock(lock_);
+ return plugins_loaded_;
+}
+
+void PluginList::RefreshPlugins() {
+ AutoLock lock(lock_);
+ plugins_need_refresh_ = true;
+}
+
+void PluginList::AddExtraPluginPath(const FilePath& plugin_path) {
+ // Chrome OS only loads plugins from /opt/google/chrome/plugins.
+#if !defined(OS_CHROMEOS)
+ AutoLock lock(lock_);
+ extra_plugin_paths_.push_back(plugin_path);
+#endif
+}
+
+void PluginList::RemoveExtraPluginPath(const FilePath& plugin_path) {
+ AutoLock lock(lock_);
+ std::vector<FilePath>::iterator it =
+ std::find(extra_plugin_paths_.begin(), extra_plugin_paths_.end(),
+ plugin_path);
+ if (it != extra_plugin_paths_.end())
+ extra_plugin_paths_.erase(it);
+}
+
+void PluginList::AddExtraPluginDir(const FilePath& plugin_dir) {
+ // Chrome OS only loads plugins from /opt/google/chrome/plugins.
+#if !defined(OS_CHROMEOS)
+ AutoLock lock(lock_);
+ extra_plugin_dirs_.push_back(plugin_dir);
+#endif
+}
+
+void PluginList::RegisterInternalPlugin(const PluginVersionInfo& info) {
+ AutoLock lock(lock_);
+ internal_plugins_.push_back(info);
+}
+
+void PluginList::UnregisterInternalPlugin(const FilePath& path) {
+ AutoLock lock(lock_);
+ for (size_t i = 0; i < internal_plugins_.size(); i++) {
+ if (internal_plugins_[i].path == path) {
+ internal_plugins_.erase(internal_plugins_.begin() + i);
+ return;
+ }
+ }
+ NOTREACHED();
+}
+
+bool PluginList::ReadPluginInfo(const FilePath& filename,
+ WebPluginInfo* info,
+ const PluginEntryPoints** entry_points) {
+ {
+ AutoLock lock(lock_);
+ for (size_t i = 0; i < internal_plugins_.size(); ++i) {
+ if (filename == internal_plugins_[i].path) {
+ *entry_points = &internal_plugins_[i].entry_points;
+ return CreateWebPluginInfo(internal_plugins_[i], info);
+ }
+ }
+ }
+
+ // Not an internal plugin.
+ *entry_points = NULL;
+
+ return PluginLib::ReadWebPluginInfo(filename, info);
+}
+
+bool PluginList::CreateWebPluginInfo(const PluginVersionInfo& pvi,
+ WebPluginInfo* info) {
+ std::vector<std::string> mime_types, file_extensions;
+ std::vector<string16> descriptions;
+ base::SplitString(WideToUTF8(pvi.mime_types), '|', &mime_types);
+ base::SplitString(WideToUTF8(pvi.file_extensions), '|', &file_extensions);
+ base::SplitString(WideToUTF16(pvi.type_descriptions), '|', &descriptions);
+
+ info->mime_types.clear();
+
+ if (mime_types.empty()) {
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Plugin " << pvi.product_name << " has no MIME types, skipping";
+ return false;
+ }
+
+ info->name = WideToUTF16(pvi.product_name);
+ info->desc = WideToUTF16(pvi.file_description);
+ info->version = WideToUTF16(pvi.file_version);
+ info->path = pvi.path;
+ info->enabled = true;
+
+ for (size_t i = 0; i < mime_types.size(); ++i) {
+ WebPluginMimeType mime_type;
+ mime_type.mime_type = StringToLowerASCII(mime_types[i]);
+ if (file_extensions.size() > i)
+ base::SplitString(file_extensions[i], ',', &mime_type.file_extensions);
+
+ if (descriptions.size() > i) {
+ mime_type.description = descriptions[i];
+
+ // On Windows, the description likely has a list of file extensions
+ // embedded in it (e.g. "SurfWriter file (*.swr)"). Remove an extension
+ // list from the description if it is present.
+ size_t ext = mime_type.description.find(ASCIIToUTF16("(*"));
+ if (ext != string16::npos) {
+ if (ext > 1 && mime_type.description[ext -1] == ' ')
+ ext--;
+
+ mime_type.description.erase(ext);
+ }
+ }
+
+ info->mime_types.push_back(mime_type);
+ }
+
+ return true;
+}
+
+PluginList::PluginList()
+ : plugins_loaded_(false),
+ plugins_need_refresh_(false),
+ disable_outdated_plugins_(false),
+ next_priority_(0) {
+ PlatformInit();
+ AddHardcodedPluginGroups();
+}
+
+bool PluginList::ShouldDisableGroup(const string16& group_name) {
+ AutoLock lock(lock_);
+ if (PluginGroup::IsPluginNameDisabledByPolicy(group_name)) {
+ disabled_groups_.insert(group_name);
+ return true;
+ }
+ return disabled_groups_.count(group_name) > 0;
+}
+
+void PluginList::LoadPlugins(bool refresh) {
+ // Don't want to hold the lock while loading new plugins, so we don't block
+ // other methods if they're called on other threads.
+ std::vector<FilePath> extra_plugin_paths;
+ std::vector<FilePath> extra_plugin_dirs;
+ std::vector<PluginVersionInfo> internal_plugins;
+ {
+ AutoLock lock(lock_);
+ if (plugins_loaded_ && !refresh && !plugins_need_refresh_)
+ return;
+
+ // Clear the refresh bit now, because it might get set again before we
+ // reach the end of the method.
+ plugins_need_refresh_ = false;
+ extra_plugin_paths = extra_plugin_paths_;
+ extra_plugin_dirs = extra_plugin_dirs_;
+ internal_plugins = internal_plugins_;
+ }
+
+ std::vector<WebPluginInfo> new_plugins;
+ std::set<FilePath> visited_plugins;
+
+ std::vector<FilePath> directories_to_scan;
+ GetPluginDirectories(&directories_to_scan);
+
+ // Load internal plugins first so that, if both an internal plugin and a
+ // "discovered" plugin want to handle the same type, the internal plugin
+ // will have precedence.
+ for (size_t i = 0; i < internal_plugins.size(); ++i) {
+ if (internal_plugins[i].path.value() == kDefaultPluginLibraryName)
+ continue;
+ LoadPlugin(internal_plugins[i].path, &new_plugins);
+ }
+
+ for (size_t i = 0; i < extra_plugin_paths.size(); ++i) {
+ const FilePath& path = extra_plugin_paths[i];
+ if (visited_plugins.find(path) != visited_plugins.end())
+ continue;
+ LoadPlugin(path, &new_plugins);
+ visited_plugins.insert(path);
+ }
+
+ for (size_t i = 0; i < extra_plugin_dirs.size(); ++i) {
+ LoadPluginsFromDir(extra_plugin_dirs[i], &new_plugins, &visited_plugins);
+ }
+
+ for (size_t i = 0; i < directories_to_scan.size(); ++i) {
+ LoadPluginsFromDir(directories_to_scan[i], &new_plugins, &visited_plugins);
+ }
+
+#if defined(OS_WIN)
+ LoadPluginsFromRegistry(&new_plugins, &visited_plugins);
+#endif
+
+ // Load the default plugin last.
+ if (webkit_glue::IsDefaultPluginEnabled())
+ LoadPlugin(FilePath(kDefaultPluginLibraryName), &new_plugins);
+
+ // Disable all of the plugins and plugin groups that are disabled by policy.
+ // There's currenly a bug that makes it impossible to correctly re-enable
+ // plugins or plugin-groups to their original, "pre-policy" state, so
+ // plugins and groups are only changed to a more "safe" state after a policy
+ // change, i.e. from enabled to disabled. See bug 54681.
+ for (PluginGroup::PluginMap::iterator it = plugin_groups_.begin();
+ it != plugin_groups_.end(); ++it) {
+ PluginGroup* group = it->second;
+ string16 group_name = group->GetGroupName();
+ if (ShouldDisableGroup(group_name)) {
+ group->Enable(false);
+ }
+
+ if (disable_outdated_plugins_) {
+ group->DisableOutdatedPlugins();
+ }
+ if (!group->Enabled()) {
+ AutoLock lock(lock_);
+ disabled_groups_.insert(group_name);
+ }
+ }
+
+ // Only update the data now since loading plugins can take a while.
+ AutoLock lock(lock_);
+
+ plugins_ = new_plugins;
+ plugins_loaded_ = true;
+}
+
+void PluginList::LoadPlugin(const FilePath& path,
+ std::vector<WebPluginInfo>* plugins) {
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Loading plugin " << path.value();
+
+ WebPluginInfo plugin_info;
+ const PluginEntryPoints* entry_points;
+
+ if (!ReadPluginInfo(path, &plugin_info, &entry_points))
+ return;
+
+ if (!ShouldLoadPlugin(plugin_info, plugins))
+ return;
+
+ if (path.value() != kDefaultPluginLibraryName
+#if defined(OS_WIN) && !defined(NDEBUG)
+ && path.BaseName().value() != L"npspy.dll" // Make an exception for NPSPY
+#endif
+ ) {
+ for (size_t i = 0; i < plugin_info.mime_types.size(); ++i) {
+ // TODO: don't load global handlers for now.
+ // WebKit hands to the Plugin before it tries
+ // to handle mimeTypes on its own.
+ const std::string &mime_type = plugin_info.mime_types[i].mime_type;
+ if (mime_type == "*" )
+ return;
+ }
+ }
+
+ // Mark disabled plugins as such. (This has to happen before calling
+ // |AddToPluginGroups(plugin_info)|.)
+ if (disabled_plugins_.count(plugin_info.path)) {
+ plugin_info.enabled = false;
+ } else {
+ plugin_info.enabled = true;
+ }
+
+ AutoLock lock(lock_);
+ plugins->push_back(plugin_info);
+ AddToPluginGroups(plugin_info);
+}
+
+bool PluginList::SupportsType(const WebPluginInfo& info,
+ const std::string &mime_type,
+ bool allow_wildcard) {
+ // Webkit will ask for a plugin to handle empty mime types.
+ if (mime_type.empty())
+ return false;
+
+ for (size_t i = 0; i < info.mime_types.size(); ++i) {
+ const WebPluginMimeType& mime_info = info.mime_types[i];
+ if (net::MatchesMimeType(mime_info.mime_type, mime_type)) {
+ if (!allow_wildcard && mime_info.mime_type == "*") {
+ continue;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PluginList::SupportsExtension(const WebPluginInfo& info,
+ const std::string &extension,
+ std::string* actual_mime_type) {
+ for (size_t i = 0; i < info.mime_types.size(); ++i) {
+ const WebPluginMimeType& mime_type = info.mime_types[i];
+ for (size_t j = 0; j < mime_type.file_extensions.size(); ++j) {
+ if (mime_type.file_extensions[j] == extension) {
+ if (actual_mime_type)
+ *actual_mime_type = mime_type.mime_type;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+void PluginList::GetPlugins(bool refresh, std::vector<WebPluginInfo>* plugins) {
+ LoadPlugins(refresh);
+
+ AutoLock lock(lock_);
+ *plugins = plugins_;
+}
+
+void PluginList::GetEnabledPlugins(bool refresh,
+ std::vector<WebPluginInfo>* plugins) {
+ LoadPlugins(refresh);
+
+ plugins->clear();
+ AutoLock lock(lock_);
+ for (std::vector<WebPluginInfo>::const_iterator it = plugins_.begin();
+ it != plugins_.end();
+ ++it) {
+ if (it->enabled)
+ plugins->push_back(*it);
+ }
+}
+
+void PluginList::GetPluginInfoArray(
+ const GURL& url,
+ const std::string& mime_type,
+ bool allow_wildcard,
+ std::vector<WebPluginInfo>* info,
+ std::vector<std::string>* actual_mime_types) {
+ DCHECK(mime_type == StringToLowerASCII(mime_type));
+ DCHECK(info);
+
+ LoadPlugins(false);
+ AutoLock lock(lock_);
+ info->clear();
+ if (actual_mime_types)
+ actual_mime_types->clear();
+
+ std::set<FilePath> visited_plugins;
+
+ // Add in enabled plugins by mime type.
+ WebPluginInfo default_plugin;
+ for (size_t i = 0; i < plugins_.size(); ++i) {
+ if (plugins_[i].enabled &&
+ SupportsType(plugins_[i], mime_type, allow_wildcard)) {
+ FilePath path = plugins_[i].path;
+ if (path.value() != kDefaultPluginLibraryName &&
+ visited_plugins.insert(path).second) {
+ info->push_back(plugins_[i]);
+ if (actual_mime_types)
+ actual_mime_types->push_back(mime_type);
+ }
+ }
+ }
+
+ // Add in enabled plugins by url.
+ std::string path = url.path();
+ std::string::size_type last_dot = path.rfind('.');
+ if (last_dot != std::string::npos) {
+ std::string extension = StringToLowerASCII(std::string(path, last_dot+1));
+ std::string actual_mime_type;
+ for (size_t i = 0; i < plugins_.size(); ++i) {
+ if (plugins_[i].enabled &&
+ SupportsExtension(plugins_[i], extension, &actual_mime_type)) {
+ FilePath path = plugins_[i].path;
+ if (path.value() != kDefaultPluginLibraryName &&
+ visited_plugins.insert(path).second) {
+ info->push_back(plugins_[i]);
+ if (actual_mime_types)
+ actual_mime_types->push_back(actual_mime_type);
+ }
+ }
+ }
+ }
+
+ // Add in disabled plugins by mime type.
+ for (size_t i = 0; i < plugins_.size(); ++i) {
+ if (!plugins_[i].enabled &&
+ SupportsType(plugins_[i], mime_type, allow_wildcard)) {
+ FilePath path = plugins_[i].path;
+ if (path.value() != kDefaultPluginLibraryName &&
+ visited_plugins.insert(path).second) {
+ info->push_back(plugins_[i]);
+ if (actual_mime_types)
+ actual_mime_types->push_back(mime_type);
+ }
+ }
+ }
+
+ // Add the default plugin at the end if it supports the mime type given,
+ // and the default plugin is enabled.
+ if (!plugins_.empty() && webkit_glue::IsDefaultPluginEnabled()) {
+ const WebPluginInfo& default_info = plugins_.back();
+ if (SupportsType(default_info, mime_type, allow_wildcard)) {
+ info->push_back(default_info);
+ if (actual_mime_types)
+ actual_mime_types->push_back(mime_type);
+ }
+ }
+}
+
+bool PluginList::GetPluginInfo(const GURL& url,
+ const std::string& mime_type,
+ bool allow_wildcard,
+ WebPluginInfo* info,
+ std::string* actual_mime_type) {
+ DCHECK(info);
+ std::vector<WebPluginInfo> info_list;
+
+ // GetPluginInfoArray has slightly less work to do if we can pass
+ // NULL for the mime type list...
+ if (actual_mime_type) {
+ std::vector<std::string> mime_type_list;
+ GetPluginInfoArray(
+ url, mime_type, allow_wildcard, &info_list, &mime_type_list);
+ if (!info_list.empty()) {
+ *info = info_list[0];
+ *actual_mime_type = mime_type_list[0];
+ return true;
+ }
+ } else {
+ GetPluginInfoArray(url, mime_type, allow_wildcard, &info_list, NULL);
+ if (!info_list.empty()) {
+ *info = info_list[0];
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PluginList::GetPluginInfoByPath(const FilePath& plugin_path,
+ WebPluginInfo* info) {
+ LoadPlugins(false);
+ AutoLock lock(lock_);
+ for (size_t i = 0; i < plugins_.size(); ++i) {
+ if (plugins_[i].path == plugin_path) {
+ *info = plugins_[i];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void PluginList::GetPluginGroups(
+ bool load_if_necessary,
+ std::vector<PluginGroup>* plugin_groups) {
+ if (load_if_necessary)
+ LoadPlugins(false);
+ plugin_groups->clear();
+ for (PluginGroup::PluginMap::const_iterator it = plugin_groups_.begin();
+ it != plugin_groups_.end(); ++it) {
+ plugin_groups->push_back(*it->second);
+ }
+}
+
+const PluginGroup* PluginList::GetPluginGroup(
+ const WebPluginInfo& web_plugin_info) {
+ AutoLock lock(lock_);
+ return AddToPluginGroups(web_plugin_info);
+}
+
+string16 PluginList::GetPluginGroupName(std::string identifier) {
+ PluginGroup::PluginMap::iterator it = plugin_groups_.find(identifier);
+ if (it == plugin_groups_.end()) {
+ return string16();
+ }
+ return it->second->GetGroupName();
+}
+
+std::string PluginList::GetPluginGroupIdentifier(
+ const WebPluginInfo& web_plugin_info) {
+ AutoLock lock(lock_);
+ PluginGroup* group = AddToPluginGroups(web_plugin_info);
+ return group->identifier();
+}
+
+void PluginList::AddHardcodedPluginGroups() {
+ AutoLock lock(lock_);
+ const PluginGroupDefinition* definitions = GetPluginGroupDefinitions();
+ for (size_t i = 0; i < GetPluginGroupDefinitionsSize(); ++i) {
+ PluginGroup* definition_group = PluginGroup::FromPluginGroupDefinition(
+ definitions[i]);
+ std::string identifier = definition_group->identifier();
+ DCHECK(plugin_groups_.find(identifier) == plugin_groups_.end());
+ plugin_groups_.insert(std::make_pair(identifier, definition_group));
+ }
+}
+
+PluginGroup* PluginList::AddToPluginGroups(
+ const WebPluginInfo& web_plugin_info) {
+ PluginGroup* group = NULL;
+ for (PluginGroup::PluginMap::iterator it = plugin_groups_.begin();
+ it != plugin_groups_.end(); ++it) {
+ if (it->second->Match(web_plugin_info))
+ group = it->second;
+ }
+ if (!group) {
+ group = PluginGroup::FromWebPluginInfo(web_plugin_info);
+ std::string identifier = group->identifier();
+ // If the identifier is not unique, use the full path. This means that we
+ // probably won't be able to search for this group by identifier, but at
+ // least it's going to be in the set of plugin groups, and if there
+ // is already a plug-in with the same filename, it's probably going to
+ // handle the same MIME types (and it has a higher priority), so this one
+ // is not going to run anyway.
+ if (plugin_groups_.find(identifier) != plugin_groups_.end())
+ identifier = PluginGroup::GetLongIdentifier(web_plugin_info);
+ DCHECK(plugin_groups_.find(identifier) == plugin_groups_.end());
+ plugin_groups_.insert(std::make_pair(identifier, group));
+ }
+ group->AddPlugin(web_plugin_info, next_priority_++);
+ return group;
+}
+
+bool PluginList::EnablePlugin(const FilePath& filename) {
+ AutoLock lock(lock_);
+
+ bool did_enable = false;
+
+ std::set<FilePath>::iterator entry = disabled_plugins_.find(filename);
+ if (entry == disabled_plugins_.end())
+ return did_enable; // Early exit if plugin not in disabled list.
+
+ disabled_plugins_.erase(entry); // Remove from disabled list.
+
+ // Set enabled flags if necessary.
+ for (std::vector<WebPluginInfo>::iterator it = plugins_.begin();
+ it != plugins_.end();
+ ++it) {
+ if (it->path == filename) {
+ DCHECK(!it->enabled); // Should have been disabled.
+ it->enabled = true;
+ did_enable = true;
+ }
+ }
+
+ return did_enable;
+}
+
+bool PluginList::DisablePlugin(const FilePath& filename) {
+ AutoLock lock(lock_);
+
+ bool did_disable = false;
+
+ if (disabled_plugins_.find(filename) != disabled_plugins_.end())
+ return did_disable; // Early exit if plugin already in disabled list.
+
+ disabled_plugins_.insert(filename); // Add to disabled list.
+
+ // Unset enabled flags if necessary.
+ for (std::vector<WebPluginInfo>::iterator it = plugins_.begin();
+ it != plugins_.end();
+ ++it) {
+ if (it->path == filename) {
+ DCHECK(it->enabled); // Should have been enabled.
+ it->enabled = false;
+ did_disable = true;
+ }
+ }
+
+ return did_disable;
+}
+
+bool PluginList::EnableGroup(bool enable, const string16& group_name) {
+ bool did_change = false;
+ {
+ AutoLock lock(lock_);
+
+ std::set<string16>::iterator entry = disabled_groups_.find(group_name);
+ if (enable) {
+ if (entry == disabled_groups_.end())
+ return did_change; // Early exit if group not in disabled list.
+ disabled_groups_.erase(entry); // Remove from disabled list.
+ } else {
+ if (entry != disabled_groups_.end())
+ return did_change; // Early exit if group already in disabled list.
+ disabled_groups_.insert(group_name);
+ }
+ }
+
+ for (PluginGroup::PluginMap::iterator it = plugin_groups_.begin();
+ it != plugin_groups_.end(); ++it) {
+ if (it->second->GetGroupName() == group_name) {
+ if (it->second->Enabled() != enable) {
+ it->second->Enable(enable);
+ did_change = true;
+ break;
+ }
+ }
+ }
+
+ return did_change;
+}
+
+void PluginList::DisableOutdatedPluginGroups() {
+ disable_outdated_plugins_ = true;
+}
+
+PluginList::~PluginList() {
+ Shutdown();
+}
+
+void PluginList::Shutdown() {
+ // TODO
+ // Note: plugin_groups_ contains simple pointers of type PluginGroup*, but
+ // since this singleton lives until the process is destroyed, no explicit
+ // cleanup is necessary.
+ // However, when running on Valgrind, we need to do the cleanup to keep the
+ // memory tree green.
+#if defined(OS_POSIX)
+ if (RUNNING_ON_VALGRIND) {
+ STLDeleteContainerPairSecondPointers(plugin_groups_.begin(),
+ plugin_groups_.end());
+ }
+#endif
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_list.h b/webkit/glue/plugins/plugin_list.h
index 111e8fa..734cc6d 100644
--- a/webkit/glue/plugins/plugin_list.h
+++ b/webkit/glue/plugins/plugin_list.h
@@ -5,26 +5,332 @@
#ifndef WEBKIT_GLUE_PLUGINS_PLUGIN_LIST_H_
#define WEBKIT_GLUE_PLUGINS_PLUGIN_LIST_H_
-// This file is here to keep NativeClient compiling. PluginList was moved to
-// webkit/plugins/npapi and into the webkit::npapi namespace. Native Client
-// depends on this old location & namespace, so we provide just enough
-// definitions here to keep it compiling until it can be updated to use the
-// new location & namespace.
-//
-// TODO(brettw) remove this flie when NaCl is updated.
+#include <set>
+#include <string>
+#include <vector>
-#include "webkit/plugins/npapi/plugin_list.h"
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/linked_ptr.h"
+#include "base/lock.h"
+#include "third_party/npapi/bindings/nphostapi.h"
+#include "webkit/glue/plugins/plugin_group.h"
+#include "webkit/glue/plugins/webplugininfo.h"
+
+class GURL;
+
+namespace base {
+
+template <typename T>
+struct DefaultLazyInstanceTraits;
+
+} // namespace base
namespace NPAPI {
-typedef webkit::npapi::PluginEntryPoints PluginEntryPoints;
-typedef webkit::npapi::PluginVersionInfo PluginVersionInfo;
+#define kDefaultPluginLibraryName FILE_PATH_LITERAL("default_plugin")
+#define kGearsPluginLibraryName FILE_PATH_LITERAL("gears")
+
+class PluginInstance;
+
+// This struct holds entry points into a plugin. The entry points are
+// slightly different between Win/Mac and Unixes.
+struct PluginEntryPoints {
+#if !defined(OS_POSIX) || defined(OS_MACOSX)
+ NP_GetEntryPointsFunc np_getentrypoints;
+#endif
+ NP_InitializeFunc np_initialize;
+ NP_ShutdownFunc np_shutdown;
+};
+
+// This struct fully describes a plugin. For external plugins, it's read in from
+// the version info of the dll; For internal plugins, it's predefined and
+// includes addresses of entry functions. (Yes, it's Win32 NPAPI-centric, but
+// it'll do for holding descriptions of internal plugins cross-platform.)
+struct PluginVersionInfo {
+ FilePath path;
+ // Info about the plugin itself.
+ std::wstring product_name;
+ std::wstring file_description;
+ std::wstring file_version;
+ // Info about the data types that the plugin supports.
+ std::wstring mime_types;
+ std::wstring file_extensions;
+ std::wstring type_descriptions;
+ // Entry points for internal plugins. Pointers are NULL for external plugins.
+ PluginEntryPoints entry_points;
+};
+// The PluginList is responsible for loading our NPAPI based plugins. It does
+// so in whatever manner is appropriate for the platform. On Windows, it loads
+// plugins from a known directory by looking for DLLs which start with "NP",
+// and checking to see if they are valid NPAPI libraries. On the Mac, it walks
+// the machine-wide and user plugin directories and loads anything that has
+// the correct types. On Linux, it walks the plugin directories as well
+// (e.g. /usr/lib/browser-plugins/).
+// This object is thread safe.
class PluginList {
public:
- static inline webkit::npapi::PluginList* Singleton() {
- return webkit::npapi::PluginList::Singleton();
- }
+ // Gets the one instance of the PluginList.
+ static PluginList* Singleton();
+
+ // Returns true if we're in debug-plugin-loading mode. This is controlled
+ // by a command line switch.
+ static bool DebugPluginLoading();
+
+ static const PluginGroupDefinition* GetPluginGroupDefinitions();
+ static size_t GetPluginGroupDefinitionsSize();
+
+ // Returns true iff the plugin list has been loaded already.
+ bool PluginsLoaded();
+
+ // Cause the plugin list to refresh next time they are accessed, regardless
+ // of whether they are already loaded.
+ void RefreshPlugins();
+
+ // Add/Remove an extra plugin to load when we actually do the loading. Must
+ // be called before the plugins have been loaded.
+ void AddExtraPluginPath(const FilePath& plugin_path);
+ void RemoveExtraPluginPath(const FilePath& plugin_path);
+
+ // Same as above, but specifies a directory in which to search for plugins.
+ void AddExtraPluginDir(const FilePath& plugin_dir);
+
+ // Register an internal plugin with the specified plugin information and
+ // function pointers. An internal plugin must be registered before it can
+ // be loaded using PluginList::LoadPlugin().
+ void RegisterInternalPlugin(const PluginVersionInfo& info);
+
+ // Removes a specified internal plugin from the list. The search will match
+ // on the path from the version info previously registered.
+ //
+ // This is generally only necessary for tests.
+ void UnregisterInternalPlugin(const FilePath& path);
+
+ // Creates a WebPluginInfo structure given a plugin's path. On success
+ // returns true, with the information being put into "info". If it's an
+ // internal plugin, "entry_points" is filled in as well with a
+ // internally-owned PluginEntryPoints pointer.
+ // Returns false if the library couldn't be found, or if it's not a plugin.
+ bool ReadPluginInfo(const FilePath& filename,
+ WebPluginInfo* info,
+ const PluginEntryPoints** entry_points);
+
+ // Populate a WebPluginInfo from a PluginVersionInfo.
+ static bool CreateWebPluginInfo(const PluginVersionInfo& pvi,
+ WebPluginInfo* info);
+
+ // Shutdown all plugins. Should be called at process teardown.
+ void Shutdown();
+
+ // Get all the plugins.
+ void GetPlugins(bool refresh, std::vector<WebPluginInfo>* plugins);
+
+ // Get all the enabled plugins.
+ void GetEnabledPlugins(bool refresh, std::vector<WebPluginInfo>* plugins);
+
+ // Returns a list in |info| containing plugins that are found for
+ // the given url and mime type (including disabled plugins, for
+ // which |info->enabled| is false). The mime type which corresponds
+ // to the URL is optionally returned back in |actual_mime_types| (if
+ // it is non-NULL), one for each of the plugin info objects found.
+ // The |allow_wildcard| parameter controls whether this function
+ // returns plugins which support wildcard mime types (* as the mime
+ // type). The |info| parameter is required to be non-NULL. The
+ // list is in order of "most desirable" to "least desirable",
+ // meaning that the default plugin is at the end of the list.
+ void GetPluginInfoArray(const GURL& url,
+ const std::string& mime_type,
+ bool allow_wildcard,
+ std::vector<WebPluginInfo>* info,
+ std::vector<std::string>* actual_mime_types);
+
+ // Returns the first item from the list returned in GetPluginInfo in |info|.
+ // Returns true if it found a match. |actual_mime_type| may be NULL.
+ bool GetPluginInfo(const GURL& url,
+ const std::string& mime_type,
+ bool allow_wildcard,
+ WebPluginInfo* info,
+ std::string* actual_mime_type);
+
+ // Get plugin info by plugin path (including disabled plugins). Returns true
+ // if the plugin is found and WebPluginInfo has been filled in |info|.
+ bool GetPluginInfoByPath(const FilePath& plugin_path,
+ WebPluginInfo* info);
+
+ // Populates the given vector with all available plugin groups.
+ void GetPluginGroups(bool load_if_necessary,
+ std::vector<PluginGroup>* plugin_groups);
+
+ // Returns the PluginGroup corresponding to the given WebPluginInfo. If no
+ // such group exists, it is created and added to the cache.
+ // Beware: when calling this from the Browser process, the group that the
+ // returned pointer points to might disappear suddenly. This happens when
+ // |RefreshPlugins()| is called and then |LoadPlugins()| is triggered by a
+ // call to |GetPlugins()|, |GetEnabledPlugins()|, |GetPluginInfoArray()|,
+ // |GetPluginInfoByPath()|, or |GetPluginGroups(true, _)|. It is the caller's
+ // responsibility to make sure this doesn't happen.
+ const PluginGroup* GetPluginGroup(const WebPluginInfo& web_plugin_info);
+
+ // Returns the name of the PluginGroup with the given identifier.
+ // If no such group exists, an empty string is returned.
+ string16 GetPluginGroupName(std::string identifier);
+
+ // Returns the identifier string of the PluginGroup corresponding to the given
+ // WebPluginInfo. If no such group exists, it is created and added to the
+ // cache.
+ std::string GetPluginGroupIdentifier(const WebPluginInfo& web_plugin_info);
+
+ // Load a specific plugin with full path.
+ void LoadPlugin(const FilePath& filename,
+ std::vector<WebPluginInfo>* plugins);
+
+ // Enable a specific plugin, specified by path. Returns |true| iff a plugin
+ // currently in the plugin list was actually enabled as a result; regardless
+ // of return value, if a plugin is found in the future with the given name, it
+ // will be enabled. Note that plugins are enabled by default as far as
+ // |PluginList| is concerned.
+ bool EnablePlugin(const FilePath& filename);
+
+ // Disable a specific plugin, specified by path. Returns |true| iff a plugin
+ // currently in the plugin list was actually disabled as a result; regardless
+ // of return value, if a plugin is found in the future with the given name, it
+ // will be disabled.
+ bool DisablePlugin(const FilePath& filename);
+
+ // Enable/disable a plugin group, specified by group_name. Returns |true| iff
+ // a plugin currently in the plugin list was actually enabled/disabled as a
+ // result; regardless of return value, if a plugin is found in the future with
+ // the given name, it will be enabled/disabled. Note that plugins are enabled
+ // by default as far as |PluginList| is concerned.
+ bool EnableGroup(bool enable, const string16& name);
+
+ // Disable all plugins groups that are known to be outdated, according to
+ // the information hardcoded in PluginGroup, to make sure that they can't
+ // be loaded on a web page and instead show a UI to update to the latest
+ // version.
+ void DisableOutdatedPluginGroups();
+
+ ~PluginList();
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(PluginGroupTest, PluginGroupDefinition);
+
+ // Constructors are private for singletons
+ PluginList();
+
+ // Creates PluginGroups for the static group definitions, and adds them to
+ // the PluginGroup cache of this PluginList.
+ void AddHardcodedPluginGroups();
+
+ // Adds the given WebPluginInfo to its corresponding group, creating it if
+ // necessary, and returns the group.
+ // Callers need to protect calls to this method by a lock themselves.
+ PluginGroup* AddToPluginGroups(const WebPluginInfo& web_plugin_info);
+
+ // Load all plugins from the default plugins directory
+ void LoadPlugins(bool refresh);
+
+ // Load all plugins from a specific directory.
+ // |plugins| is updated with loaded plugin information.
+ // |visited_plugins| is updated with paths to all plugins that were considered
+ // (including those we didn't load)
+ void LoadPluginsFromDir(const FilePath& path,
+ std::vector<WebPluginInfo>* plugins,
+ std::set<FilePath>* visited_plugins);
+
+ // Returns true if we should load the given plugin, or false otherwise.
+ // plugins is the list of plugins we have crawled in the current plugin
+ // loading run.
+ bool ShouldLoadPlugin(const WebPluginInfo& info,
+ std::vector<WebPluginInfo>* plugins);
+
+ // Return whether a plug-in group with the given name should be disabled,
+ // either because it already is on the list of disabled groups, or because it
+ // is blacklisted by a policy. In the latter case, add the plugin group to the
+ // list of disabled groups as well.
+ bool ShouldDisableGroup(const string16& group_name);
+
+ // Returns true if the given WebPluginInfo supports "mime-type".
+ // mime_type should be all lower case.
+ static bool SupportsType(const WebPluginInfo& info,
+ const std::string &mime_type,
+ bool allow_wildcard);
+
+ // Returns true if the given WebPluginInfo supports a given file extension.
+ // extension should be all lower case.
+ // If mime_type is not NULL, it will be set to the mime type if found.
+ // The mime type which corresponds to the extension is optionally returned
+ // back.
+ static bool SupportsExtension(const WebPluginInfo& info,
+ const std::string &extension,
+ std::string* actual_mime_type);
+
+ //
+ // Platform functions
+ //
+
+ // Do any initialization.
+ void PlatformInit();
+
+ // Get the ordered list of directories from which to load plugins
+ void GetPluginDirectories(std::vector<FilePath>* plugin_dirs);
+
+ //
+ // Command-line switches
+ //
+
+#if defined(OS_WIN)
+ // true if we shouldn't load the new WMP plugin.
+ bool dont_load_new_wmp_;
+
+ // Loads plugins registered under HKCU\Software\MozillaPlugins and
+ // HKLM\Software\MozillaPlugins.
+ void LoadPluginsFromRegistry(std::vector<WebPluginInfo>* plugins,
+ std::set<FilePath>* visited_plugins);
+#endif
+
+ //
+ // Internals
+ //
+
+ bool plugins_loaded_;
+
+ // If true, we reload plugins even if they've been loaded already.
+ bool plugins_need_refresh_;
+
+ // Contains information about the available plugins.
+ std::vector<WebPluginInfo> plugins_;
+
+ // Extra plugin paths that we want to search when loading.
+ std::vector<FilePath> extra_plugin_paths_;
+
+ // Extra plugin directories that we want to search when loading.
+ std::vector<FilePath> extra_plugin_dirs_;
+
+ // Holds information about internal plugins.
+ std::vector<PluginVersionInfo> internal_plugins_;
+
+ // Path names of plugins to disable (the default is to enable them all).
+ std::set<FilePath> disabled_plugins_;
+
+ // Group names to disable (the default is to enable them all).
+ std::set<string16> disabled_groups_;
+
+ bool disable_outdated_plugins_;
+
+ // Holds the currently available plugin groups.
+ PluginGroup::PluginMap plugin_groups_;
+
+ int next_priority_;
+
+ // Need synchronization for the above members since this object can be
+ // accessed on multiple threads.
+ Lock lock_;
+
+ friend struct base::DefaultLazyInstanceTraits<PluginList>;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginList);
};
} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_list_mac.mm b/webkit/glue/plugins/plugin_list_mac.mm
new file mode 100644
index 0000000..e7a2337
--- /dev/null
+++ b/webkit/glue/plugins/plugin_list_mac.mm
@@ -0,0 +1,109 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_list.h"
+
+#import <Foundation/Foundation.h>
+
+#include "base/file_util.h"
+#include "base/mac_util.h"
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+
+namespace {
+
+void GetPluginCommonDirectory(std::vector<FilePath>* plugin_dirs,
+ bool user) {
+ // Note that there are no NSSearchPathDirectory constants for these
+ // directories so we can't use Cocoa's NSSearchPathForDirectoriesInDomains().
+ // Interestingly, Safari hard-codes the location (see
+ // WebKit/WebKit/mac/Plugins/WebPluginDatabase.mm's +_defaultPlugInPaths).
+ FSRef ref;
+ OSErr err = FSFindFolder(user ? kUserDomain : kLocalDomain,
+ kInternetPlugInFolderType, false, &ref);
+
+ if (err)
+ return;
+
+ plugin_dirs->push_back(FilePath(mac_util::PathFromFSRef(ref)));
+}
+
+// Returns true if the plugin should be prevented from loading.
+bool IsBlacklistedPlugin(const WebPluginInfo& info) {
+ // We blacklist Gears by included MIME type, since that is more stable than
+ // its name. Be careful about adding any more plugins to this list though,
+ // since it's easy to accidentally blacklist plugins that support lots of
+ // MIME types.
+ for (std::vector<WebPluginMimeType>::const_iterator i =
+ info.mime_types.begin(); i != info.mime_types.end(); ++i) {
+ // The Gears plugin is Safari-specific, so don't load it.
+ if (i->mime_type == "application/x-googlegears")
+ return true;
+ }
+
+ // Versions of Flip4Mac 2.3 before 2.3.6 often hang the renderer, so don't
+ // load them.
+ if (StartsWith(info.name, ASCIIToUTF16("Flip4Mac Windows Media"), false) &&
+ StartsWith(info.version, ASCIIToUTF16("2.3"), false)) {
+ std::vector<string16> components;
+ base::SplitString(info.version, '.', &components);
+ int bugfix_version = 0;
+ return (components.size() >= 3 &&
+ base::StringToInt(components[2], &bugfix_version) &&
+ bugfix_version < 6);
+ }
+
+ return false;
+}
+
+} // namespace
+
+namespace NPAPI
+{
+
+void PluginList::PlatformInit() {
+}
+
+void PluginList::GetPluginDirectories(std::vector<FilePath>* plugin_dirs) {
+ // Load from the user's area
+ GetPluginCommonDirectory(plugin_dirs, true);
+
+ // Load from the machine-wide area
+ GetPluginCommonDirectory(plugin_dirs, false);
+}
+
+void PluginList::LoadPluginsFromDir(const FilePath &path,
+ std::vector<WebPluginInfo>* plugins,
+ std::set<FilePath>* visited_plugins) {
+ file_util::FileEnumerator enumerator(path,
+ false, // not recursive
+ file_util::FileEnumerator::DIRECTORIES);
+ for (FilePath path = enumerator.Next(); !path.value().empty();
+ path = enumerator.Next()) {
+ LoadPlugin(path, plugins);
+ visited_plugins->insert(path);
+ }
+}
+
+bool PluginList::ShouldLoadPlugin(const WebPluginInfo& info,
+ std::vector<WebPluginInfo>* plugins) {
+ if (IsBlacklistedPlugin(info))
+ return false;
+
+ // Hierarchy check
+ // (we're loading plugins hierarchically from Library folders, so plugins we
+ // encounter earlier must override plugins we encounter later)
+ for (size_t i = 0; i < plugins->size(); ++i) {
+ if ((*plugins)[i].path.BaseName() == info.path.BaseName()) {
+ return false; // We already have a loaded plugin higher in the hierarchy.
+ }
+ }
+
+ return true;
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_list_posix.cc b/webkit/glue/plugins/plugin_list_posix.cc
new file mode 100644
index 0000000..654c0c5
--- /dev/null
+++ b/webkit/glue/plugins/plugin_list_posix.cc
@@ -0,0 +1,270 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_list.h"
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/sha1.h"
+#include "base/string_split.h"
+#include "base/string_util.h"
+#include "build/build_config.h"
+
+namespace {
+
+// We build up a list of files and mtimes so we can sort them.
+typedef std::pair<FilePath, base::Time> FileAndTime;
+typedef std::vector<FileAndTime> FileTimeList;
+
+// Comparator used to sort by descending mtime then ascending filename.
+bool CompareTime(const FileAndTime& a, const FileAndTime& b) {
+ if (a.second == b.second) {
+ // Fall back on filename sorting, just to make the predicate valid.
+ return a.first < b.first;
+ }
+
+ // Sort by mtime, descending.
+ return a.second > b.second;
+}
+
+// Return true if |path| matches a known (file size, sha1sum) pair.
+// The use of the file size is an optimization so we don't have to read in
+// the entire file unless we have to.
+bool IsBlacklistedBySha1sum(const FilePath& path) {
+ const struct BadEntry {
+ int64 size;
+ std::string sha1;
+ } bad_entries[] = {
+ // Flash 9 r31 - http://crbug.com/29237
+ { 7040080, "fa5803061125ca47846713b34a26a42f1f1e98bb" },
+ // Flash 9 r48 - http://crbug.com/29237
+ { 7040036, "0c4b3768a6d4bfba003088e4b9090d381de1af2b" },
+ };
+
+ int64 size;
+ if (!file_util::GetFileSize(path, &size))
+ return false;
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(bad_entries); i++) {
+ if (bad_entries[i].size != size)
+ continue;
+
+ std::string file_content;
+ if (!file_util::ReadFileToString(path, &file_content))
+ continue;
+ std::string sha1 = base::SHA1HashString(file_content);
+ std::string sha1_readable;
+ for (size_t j = 0; j < sha1.size(); j++)
+ base::StringAppendF(&sha1_readable, "%02x", sha1[j] & 0xFF);
+ if (bad_entries[i].sha1 == sha1_readable)
+ return true;
+ }
+ return false;
+}
+
+// Some plugins are shells around other plugins; we prefer to use the
+// real plugin directly, if it's available. This function returns
+// true if we should prefer other plugins over this one. We'll still
+// use a "undesirable" plugin if no other option is available.
+bool IsUndesirablePlugin(const WebPluginInfo& info) {
+ std::string filename = info.path.BaseName().value();
+ const char* kUndesiredPlugins[] = {
+ "npcxoffice", // Crossover
+ "npwrapper", // nspluginwrapper
+ };
+ for (size_t i = 0; i < arraysize(kUndesiredPlugins); i++) {
+ if (filename.find(kUndesiredPlugins[i]) != std::string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Return true if we shouldn't load a plugin at all.
+// This is an ugly hack to blacklist Adobe Acrobat due to not supporting
+// its Xt-based mainloop.
+// http://code.google.com/p/chromium/issues/detail?id=38229
+// The gecko-mediaplayer plugins also crashes the entire browser sometimes.
+// http://code.google.com/p/chromium/issues/detail?id=24507
+bool IsBlacklistedPlugin(const FilePath& path) {
+ const char* kBlackListedPlugins[] = {
+ "nppdf.so", // Adobe PDF
+ "gecko-mediaplayer", // Gecko Media Player
+ };
+ std::string filename = path.BaseName().value();
+ for (size_t i = 0; i < arraysize(kBlackListedPlugins); i++) {
+ if (filename.find(kBlackListedPlugins[i]) != std::string::npos) {
+ return true;
+ }
+ }
+ return IsBlacklistedBySha1sum(path);
+}
+
+} // anonymous namespace
+
+namespace NPAPI {
+
+void PluginList::PlatformInit() {
+}
+
+void PluginList::GetPluginDirectories(std::vector<FilePath>* plugin_dirs) {
+ // See http://groups.google.com/group/chromium-dev/browse_thread/thread/7a70e5fcbac786a9
+ // for discussion.
+ // We first consult Chrome-specific dirs, then fall back on the logic
+ // Mozilla uses.
+
+ // Note: "extra" plugin dirs, including the Plugins subdirectory of
+ // your Chrome config, are examined before these. See the logic
+ // related to extra_plugin_dirs in plugin_list.cc.
+
+ // The Chrome binary dir + "plugins/".
+ FilePath dir;
+ PathService::Get(base::DIR_EXE, &dir);
+ plugin_dirs->push_back(dir.Append("plugins"));
+
+ // Chrome OS only loads plugins from /opt/google/chrome/plugins.
+#if !defined(OS_CHROMEOS)
+ // Mozilla code to reference:
+ // http://mxr.mozilla.org/firefox/ident?i=NS_APP_PLUGINS_DIR_LIST
+ // and tens of accompanying files (mxr is very helpful).
+ // This code carefully matches their behavior for compat reasons.
+
+ // 1) MOZ_PLUGIN_PATH env variable.
+ const char* moz_plugin_path = getenv("MOZ_PLUGIN_PATH");
+ if (moz_plugin_path) {
+ std::vector<std::string> paths;
+ base::SplitString(moz_plugin_path, ':', &paths);
+ for (size_t i = 0; i < paths.size(); ++i)
+ plugin_dirs->push_back(FilePath(paths[i]));
+ }
+
+ // 2) NS_USER_PLUGINS_DIR: ~/.mozilla/plugins.
+ // This is a de-facto standard, so even though we're not Mozilla, let's
+ // look in there too.
+ FilePath home = file_util::GetHomeDir();
+ if (!home.empty())
+ plugin_dirs->push_back(home.Append(".mozilla/plugins"));
+
+ // 3) NS_SYSTEM_PLUGINS_DIR:
+ // This varies across different browsers and versions, so check 'em all.
+ plugin_dirs->push_back(FilePath("/usr/lib/browser-plugins"));
+ plugin_dirs->push_back(FilePath("/usr/lib/mozilla/plugins"));
+ plugin_dirs->push_back(FilePath("/usr/lib/firefox/plugins"));
+ plugin_dirs->push_back(FilePath("/usr/lib/xulrunner-addons/plugins"));
+
+#if defined(ARCH_CPU_64_BITS)
+ // On my Ubuntu system, /usr/lib64 is a symlink to /usr/lib.
+ // But a user reported on their Fedora system they are separate.
+ plugin_dirs->push_back(FilePath("/usr/lib64/browser-plugins"));
+ plugin_dirs->push_back(FilePath("/usr/lib64/mozilla/plugins"));
+ plugin_dirs->push_back(FilePath("/usr/lib64/firefox/plugins"));
+ plugin_dirs->push_back(FilePath("/usr/lib64/xulrunner-addons/plugins"));
+#endif // defined(ARCH_CPU_64_BITS)
+#endif // !defined(OS_CHROMEOS)
+}
+
+void PluginList::LoadPluginsFromDir(const FilePath& dir_path,
+ std::vector<WebPluginInfo>* plugins,
+ std::set<FilePath>* visited_plugins) {
+ // See ScanPluginsDirectory near
+ // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginHostImpl.cpp#5052
+
+ // Construct and stat a list of all filenames under consideration, for
+ // later sorting by mtime.
+ FileTimeList files;
+ file_util::FileEnumerator enumerator(dir_path,
+ false, // not recursive
+ file_util::FileEnumerator::FILES);
+ for (FilePath path = enumerator.Next(); !path.value().empty();
+ path = enumerator.Next()) {
+ // Skip over Mozilla .xpt files.
+ if (path.MatchesExtension(FILE_PATH_LITERAL(".xpt")))
+ continue;
+
+ // Java doesn't like being loaded through a symlink, since it uses
+ // its path to find dependent data files.
+ // file_util::AbsolutePath calls through to realpath(), which resolves
+ // symlinks.
+ FilePath orig_path = path;
+ file_util::AbsolutePath(&path);
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Resolved " << orig_path.value() << " -> " << path.value();
+
+ if (visited_plugins->find(path) != visited_plugins->end()) {
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Skipping duplicate instance of " << path.value();
+ continue;
+ }
+ visited_plugins->insert(path);
+
+ if (IsBlacklistedPlugin(path)) {
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Skipping blacklisted plugin " << path.value();
+ continue;
+ }
+
+ // Flash stops working if the containing directory involves 'netscape'.
+ // No joke. So use the other path if it's better.
+ static const char kFlashPlayerFilename[] = "libflashplayer.so";
+ static const char kNetscapeInPath[] = "/netscape/";
+ if (path.BaseName().value() == kFlashPlayerFilename &&
+ path.value().find(kNetscapeInPath) != std::string::npos) {
+ if (orig_path.value().find(kNetscapeInPath) == std::string::npos) {
+ // Go back to the old path.
+ path = orig_path;
+ } else {
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Flash misbehaves when used from a directory containing "
+ << kNetscapeInPath << ", so skipping " << orig_path.value();
+ continue;
+ }
+ }
+
+ // Get mtime.
+ base::PlatformFileInfo info;
+ if (!file_util::GetFileInfo(path, &info))
+ continue;
+
+ files.push_back(std::make_pair(path, info.last_modified));
+ }
+
+ // Sort the file list by time (and filename).
+ std::sort(files.begin(), files.end(), CompareTime);
+
+ // Load the files in order.
+ for (FileTimeList::const_iterator i = files.begin(); i != files.end(); ++i) {
+ LoadPlugin(i->first, plugins);
+ }
+}
+
+bool PluginList::ShouldLoadPlugin(const WebPluginInfo& info,
+ std::vector<WebPluginInfo>* plugins) {
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Considering " << info.path.value() << " (" << info.name << ")";
+
+ if (IsUndesirablePlugin(info)) {
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << info.path.value() << " is undesirable.";
+
+ // See if we have a better version of this plugin.
+ for (size_t i = 0; i < plugins->size(); ++i) {
+ if (plugins->at(i).name == info.name &&
+ !IsUndesirablePlugin(plugins->at(i))) {
+ // Skip the current undesirable one so we can use the better one
+ // we just found.
+ LOG_IF(ERROR, PluginList::DebugPluginLoading())
+ << "Skipping " << info.path.value() << ", preferring "
+ << plugins->at(i).path.value();
+ return false;
+ }
+ }
+ }
+
+ // TODO(evanm): prefer the newest version of flash, etc. here?
+
+ VLOG_IF(1, PluginList::DebugPluginLoading()) << "Using " << info.path.value();
+
+ return true;
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_list_win.cc b/webkit/glue/plugins/plugin_list_win.cc
new file mode 100644
index 0000000..4869262
--- /dev/null
+++ b/webkit/glue/plugins/plugin_list_win.cc
@@ -0,0 +1,410 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_list.h"
+
+#include <tchar.h>
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/scoped_ptr.h"
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+#include "base/string_util.h"
+#include "base/win/registry.h"
+#include "webkit/glue/plugins/plugin_constants_win.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+#include "webkit/glue/webkit_glue.h"
+
+namespace {
+
+const TCHAR kRegistryApps[] =
+ _T("Software\\Microsoft\\Windows\\CurrentVersion\\App Paths");
+const TCHAR kRegistryFirefox[] = _T("firefox.exe");
+const TCHAR kRegistryAcrobat[] = _T("Acrobat.exe");
+const TCHAR kRegistryAcrobatReader[] = _T("AcroRd32.exe");
+const TCHAR kRegistryWindowsMedia[] = _T("wmplayer.exe");
+const TCHAR kRegistryQuickTime[] = _T("QuickTimePlayer.exe");
+const TCHAR kRegistryPath[] = _T("Path");
+const TCHAR kRegistryFirefoxInstalled[] =
+ _T("SOFTWARE\\Mozilla\\Mozilla Firefox");
+const TCHAR kRegistryJava[] =
+ _T("Software\\JavaSoft\\Java Runtime Environment");
+const TCHAR kRegistryBrowserJavaVersion[] = _T("BrowserJavaVersion");
+const TCHAR kRegistryCurrentJavaVersion[] = _T("CurrentVersion");
+const TCHAR kRegistryJavaHome[] = _T("JavaHome");
+const TCHAR kJavaDeploy1[] = _T("npdeploytk.dll");
+const TCHAR kJavaDeploy2[] = _T("npdeployjava1.dll");
+
+// The application path where we expect to find plugins.
+void GetAppDirectory(std::set<FilePath>* plugin_dirs) {
+ FilePath app_path;
+ if (!webkit_glue::GetApplicationDirectory(&app_path))
+ return;
+
+ app_path = app_path.AppendASCII("plugins");
+ plugin_dirs->insert(app_path);
+}
+
+// The executable path where we expect to find plugins.
+void GetExeDirectory(std::set<FilePath>* plugin_dirs) {
+ FilePath exe_path;
+ if (!webkit_glue::GetExeDirectory(&exe_path))
+ return;
+
+ exe_path = exe_path.AppendASCII("plugins");
+ plugin_dirs->insert(exe_path);
+}
+
+// Gets the installed path for a registered app.
+bool GetInstalledPath(const TCHAR* app, FilePath* out) {
+ std::wstring reg_path(kRegistryApps);
+ reg_path.append(L"\\");
+ reg_path.append(app);
+
+ base::win::RegKey key(HKEY_LOCAL_MACHINE, reg_path.c_str(), KEY_READ);
+ std::wstring path;
+ if (key.ReadValue(kRegistryPath, &path)) {
+ *out = FilePath(path);
+ return true;
+ }
+
+ return false;
+}
+
+// Search the registry at the given path and detect plugin directories.
+void GetPluginsInRegistryDirectory(
+ HKEY root_key,
+ const std::wstring& registry_folder,
+ std::set<FilePath>* plugin_dirs) {
+ for (base::win::RegistryKeyIterator iter(root_key, registry_folder.c_str());
+ iter.Valid(); ++iter) {
+ // Use the registry to gather plugin across the file system.
+ std::wstring reg_path = registry_folder;
+ reg_path.append(L"\\");
+ reg_path.append(iter.Name());
+ base::win::RegKey key(root_key, reg_path.c_str(), KEY_READ);
+
+ std::wstring path;
+ if (key.ReadValue(kRegistryPath, &path))
+ plugin_dirs->insert(FilePath(path));
+ }
+}
+
+// Enumerate through the registry key to find all installed FireFox paths.
+// FireFox 3 beta and version 2 can coexist. See bug: 1025003
+void GetFirefoxInstalledPaths(std::vector<FilePath>* out) {
+ base::win::RegistryKeyIterator it(HKEY_LOCAL_MACHINE,
+ kRegistryFirefoxInstalled);
+ for (; it.Valid(); ++it) {
+ std::wstring full_path = std::wstring(kRegistryFirefoxInstalled) + L"\\" +
+ it.Name() + L"\\Main";
+ base::win::RegKey key(HKEY_LOCAL_MACHINE, full_path.c_str(), KEY_READ);
+ std::wstring install_dir;
+ if (!key.ReadValue(L"Install Directory", &install_dir))
+ continue;
+ out->push_back(FilePath(install_dir));
+ }
+}
+
+// Get plugin directory locations from the Firefox install path. This is kind
+// of a kludge, but it helps us locate the flash player for users that
+// already have it for firefox. Not having to download yet-another-plugin
+// is a good thing.
+void GetFirefoxDirectory(std::set<FilePath>* plugin_dirs) {
+ std::vector<FilePath> paths;
+ GetFirefoxInstalledPaths(&paths);
+ for (unsigned int i = 0; i < paths.size(); ++i) {
+ plugin_dirs->insert(paths[i].Append(L"plugins"));
+ }
+
+ FilePath firefox_app_data_plugin_path;
+ if (PathService::Get(base::DIR_APP_DATA, &firefox_app_data_plugin_path)) {
+ firefox_app_data_plugin_path =
+ firefox_app_data_plugin_path.AppendASCII("Mozilla")
+ .AppendASCII("plugins");
+ plugin_dirs->insert(firefox_app_data_plugin_path);
+ }
+}
+
+// Hardcoded logic to detect Acrobat plugins locations.
+void GetAcrobatDirectory(std::set<FilePath>* plugin_dirs) {
+ FilePath path;
+ if (!GetInstalledPath(kRegistryAcrobatReader, &path) &&
+ !GetInstalledPath(kRegistryAcrobat, &path)) {
+ return;
+ }
+
+ plugin_dirs->insert(path.Append(L"Browser"));
+}
+
+// Hardcoded logic to detect QuickTime plugin location.
+void GetQuicktimeDirectory(std::set<FilePath>* plugin_dirs) {
+ FilePath path;
+ if (GetInstalledPath(kRegistryQuickTime, &path))
+ plugin_dirs->insert(path.Append(L"plugins"));
+}
+
+// Hardcoded logic to detect Windows Media Player plugin location.
+void GetWindowsMediaDirectory(std::set<FilePath>* plugin_dirs) {
+ FilePath path;
+ if (GetInstalledPath(kRegistryWindowsMedia, &path))
+ plugin_dirs->insert(path);
+
+ // If the Windows Media Player Firefox plugin is installed before Firefox,
+ // the plugin will get written under PFiles\Plugins on one the drives
+ // (usually, but not always, the last letter).
+ int size = GetLogicalDriveStrings(0, NULL);
+ if (size) {
+ scoped_array<wchar_t> strings(new wchar_t[size]);
+ if (GetLogicalDriveStrings(size, strings.get())) {
+ wchar_t* next_drive = strings.get();
+ while (*next_drive) {
+ if (GetDriveType(next_drive) == DRIVE_FIXED) {
+ FilePath pfiles(next_drive);
+ pfiles = pfiles.Append(L"PFiles\\Plugins");
+ if (file_util::PathExists(pfiles))
+ plugin_dirs->insert(pfiles);
+ }
+ next_drive = &next_drive[wcslen(next_drive) + 1];
+ }
+ }
+ }
+}
+
+// Hardcoded logic to detect Java plugin location.
+void GetJavaDirectory(std::set<FilePath>* plugin_dirs) {
+ // Load the new NPAPI Java plugin
+ // 1. Open the main JRE key under HKLM
+ base::win::RegKey java_key(HKEY_LOCAL_MACHINE, kRegistryJava,
+ KEY_QUERY_VALUE);
+
+ // 2. Read the current Java version
+ std::wstring java_version;
+ if (!java_key.ReadValue(kRegistryBrowserJavaVersion, &java_version))
+ java_key.ReadValue(kRegistryCurrentJavaVersion, &java_version);
+
+ if (!java_version.empty()) {
+ java_key.OpenKey(java_version.c_str(), KEY_QUERY_VALUE);
+
+ // 3. Install path of the JRE binaries is specified in "JavaHome"
+ // value under the Java version key.
+ std::wstring java_plugin_directory;
+ if (java_key.ReadValue(kRegistryJavaHome, &java_plugin_directory)) {
+ // 4. The new plugin resides under the 'bin/new_plugin'
+ // subdirectory.
+ DCHECK(!java_plugin_directory.empty());
+ java_plugin_directory.append(L"\\bin\\new_plugin");
+
+ // 5. We don't know the exact name of the DLL but it's in the form
+ // NP*.dll so just invoke LoadPlugins on this path.
+ plugin_dirs->insert(FilePath(java_plugin_directory));
+ }
+ }
+}
+
+} // anonymous namespace
+
+namespace NPAPI {
+
+void PluginList::PlatformInit() {
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ dont_load_new_wmp_ = command_line.HasSwitch(kUseOldWMPPluginSwitch);
+}
+
+void PluginList::GetPluginDirectories(std::vector<FilePath>* plugin_dirs) {
+ // We use a set for uniqueness, which we require, over order, which we do not.
+ std::set<FilePath> dirs;
+
+ // Load from the application-specific area
+ GetAppDirectory(&dirs);
+
+ // Load from the executable area
+ GetExeDirectory(&dirs);
+
+ // Load Java
+ GetJavaDirectory(&dirs);
+
+ // Load firefox plugins too. This is mainly to try to locate
+ // a pre-installed Flash player.
+ GetFirefoxDirectory(&dirs);
+
+ // Firefox hard-codes the paths of some popular plugins to ensure that
+ // the plugins are found. We are going to copy this as well.
+ GetAcrobatDirectory(&dirs);
+ GetQuicktimeDirectory(&dirs);
+ GetWindowsMediaDirectory(&dirs);
+
+ for (std::set<FilePath>::iterator i = dirs.begin(); i != dirs.end(); ++i)
+ plugin_dirs->push_back(*i);
+}
+
+void PluginList::LoadPluginsFromDir(const FilePath &path,
+ std::vector<WebPluginInfo>* plugins,
+ std::set<FilePath>* visited_plugins) {
+ WIN32_FIND_DATA find_file_data;
+ HANDLE find_handle;
+
+ std::wstring dir = path.value();
+ // FindFirstFile requires that you specify a wildcard for directories.
+ dir.append(L"\\NP*.DLL");
+
+ find_handle = FindFirstFile(dir.c_str(), &find_file_data);
+ if (find_handle == INVALID_HANDLE_VALUE)
+ return;
+
+ do {
+ if (!(find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+ FilePath filename = path.Append(find_file_data.cFileName);
+ LoadPlugin(filename, plugins);
+ visited_plugins->insert(filename);
+ }
+ } while (FindNextFile(find_handle, &find_file_data) != 0);
+
+ DCHECK(GetLastError() == ERROR_NO_MORE_FILES);
+ FindClose(find_handle);
+}
+
+void PluginList::LoadPluginsFromRegistry(
+ std::vector<WebPluginInfo>* plugins,
+ std::set<FilePath>* visited_plugins) {
+ std::set<FilePath> plugin_dirs;
+
+ GetPluginsInRegistryDirectory(
+ HKEY_CURRENT_USER, kRegistryMozillaPlugins, &plugin_dirs);
+ GetPluginsInRegistryDirectory(
+ HKEY_LOCAL_MACHINE, kRegistryMozillaPlugins, &plugin_dirs);
+
+ for (std::set<FilePath>::iterator i = plugin_dirs.begin();
+ i != plugin_dirs.end(); ++i) {
+ LoadPlugin(*i, plugins);
+ visited_plugins->insert(*i);
+ }
+}
+
+// Returns true if the given plugins share at least one mime type. This is used
+// to differentiate newer versions of a plugin vs two plugins which happen to
+// have the same filename.
+bool HaveSharedMimeType(const WebPluginInfo& plugin1,
+ const WebPluginInfo& plugin2) {
+ for (size_t i = 0; i < plugin1.mime_types.size(); ++i) {
+ for (size_t j = 0; j < plugin2.mime_types.size(); ++j) {
+ if (plugin1.mime_types[i].mime_type == plugin2.mime_types[j].mime_type)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Compares Windows style version strings (i.e. 1,2,3,4). Returns true if b's
+// version is newer than a's, or false if it's equal or older.
+bool IsNewerVersion(const std::wstring& a, const std::wstring& b) {
+ std::vector<std::wstring> a_ver, b_ver;
+ base::SplitString(a, ',', &a_ver);
+ base::SplitString(b, ',', &b_ver);
+ if (a_ver.size() == 1 && b_ver.size() == 1) {
+ a_ver.clear();
+ b_ver.clear();
+ base::SplitString(a, '.', &a_ver);
+ base::SplitString(b, '.', &b_ver);
+ }
+ if (a_ver.size() != b_ver.size())
+ return false;
+ for (size_t i = 0; i < a_ver.size(); i++) {
+ int cur_a, cur_b;
+ base::StringToInt(a_ver[i], &cur_a);
+ base::StringToInt(b_ver[i], &cur_b);
+
+ if (cur_a > cur_b)
+ return false;
+ if (cur_a < cur_b)
+ return true;
+ }
+ return false;
+}
+
+bool PluginList::ShouldLoadPlugin(const WebPluginInfo& info,
+ std::vector<WebPluginInfo>* plugins) {
+ // Version check
+
+ for (size_t i = 0; i < plugins->size(); ++i) {
+ std::wstring plugin1 =
+ StringToLowerASCII((*plugins)[i].path.BaseName().ToWStringHack());
+ std::wstring plugin2 =
+ StringToLowerASCII(info.path.BaseName().ToWStringHack());
+ if ((plugin1 == plugin2 && HaveSharedMimeType((*plugins)[i], info)) ||
+ (plugin1 == kJavaDeploy1 && plugin2 == kJavaDeploy2) ||
+ (plugin1 == kJavaDeploy2 && plugin2 == kJavaDeploy1)) {
+ if (!IsNewerVersion((*plugins)[i].version, info.version))
+ return false; // We have loaded a plugin whose version is newer.
+
+ plugins->erase(plugins->begin() + i);
+ break;
+ }
+ }
+
+ // Troublemakers
+
+ std::wstring filename = StringToLowerASCII(info.path.BaseName().value());
+ // Depends on XPCOM.
+ if (filename == kMozillaActiveXPlugin)
+ return false;
+
+ // Disable the Yahoo Application State plugin as it crashes the plugin
+ // process on return from NPObjectStub::OnInvoke. Please refer to
+ // http://b/issue?id=1372124 for more information.
+ if (filename == kYahooApplicationStatePlugin)
+ return false;
+
+ // Disable the WangWang protocol handler plugin (npww.dll) as it crashes
+ // chrome during shutdown. Firefox also disables this plugin.
+ // Please refer to http://code.google.com/p/chromium/issues/detail?id=3953
+ // for more information.
+ if (filename == kWanWangProtocolHandlerPlugin)
+ return false;
+
+ // We only work with newer versions of the Java plugin which use NPAPI only
+ // and don't depend on XPCOM.
+ if (filename == kJavaPlugin1 || filename == kJavaPlugin2) {
+ std::vector<std::wstring> ver;
+ base::SplitString(info.version, '.', &ver);
+ int major, minor, update;
+ if (ver.size() == 4 &&
+ base::StringToInt(ver[0], &major) &&
+ base::StringToInt(ver[1], &minor) &&
+ base::StringToInt(ver[2], &update)) {
+ if (major == 6 && minor == 0 && update < 120)
+ return false; // Java SE6 Update 11 or older.
+ }
+ }
+
+ // Special WMP handling
+
+ // If both the new and old WMP plugins exist, only load the new one.
+ if (filename == kNewWMPPlugin) {
+ if (dont_load_new_wmp_)
+ return false;
+
+ for (size_t i = 0; i < plugins->size(); ++i) {
+ if ((*plugins)[i].path.BaseName().value() == kOldWMPPlugin) {
+ plugins->erase(plugins->begin() + i);
+ break;
+ }
+ }
+ } else if (filename == kOldWMPPlugin) {
+ for (size_t i = 0; i < plugins->size(); ++i) {
+ if ((*plugins)[i].path.BaseName().value() == kNewWMPPlugin)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_stream.cc b/webkit/glue/plugins/plugin_stream.cc
new file mode 100644
index 0000000..e465e2d
--- /dev/null
+++ b/webkit/glue/plugins/plugin_stream.cc
@@ -0,0 +1,258 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO : Support NP_ASFILEONLY mode
+// TODO : Support NP_SEEK mode
+// TODO : Support SEEKABLE=true in NewStream
+
+#include "webkit/glue/plugins/plugin_stream.h"
+
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "net/base/mime_util.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "googleurl/src/gurl.h"
+
+namespace NPAPI {
+
+PluginStream::~PluginStream() {
+ // always close our temporary files.
+ CloseTempFile();
+ free(const_cast<char*>(stream_.url));
+}
+
+bool PluginStream::Open(const std::string &mime_type,
+ const std::string &headers,
+ uint32 length,
+ uint32 last_modified,
+ bool request_is_seekable) {
+ headers_ = headers;
+ NPP id = instance_->npp();
+ stream_.end = length;
+ stream_.lastmodified = last_modified;
+ stream_.pdata = 0;
+ stream_.ndata = id->ndata;
+ stream_.notifyData = notify_data_;
+ if (!headers_.empty())
+ stream_.headers = headers_.c_str();
+
+ bool seekable_stream = false;
+ if (request_is_seekable) {
+ std::string headers_lc = StringToLowerASCII(headers);
+ if (headers_lc.find("accept-ranges: bytes") != std::string::npos) {
+ seekable_stream = true;
+ }
+ }
+
+ const char *char_mime_type = "application/x-unknown-content-type";
+ std::string temp_mime_type;
+ if (!mime_type.empty()) {
+ char_mime_type = mime_type.c_str();
+ } else {
+ GURL gurl(stream_.url);
+
+#if defined(OS_WIN)
+ FilePath path(UTF8ToWide(gurl.path()));
+#elif defined(OS_POSIX)
+ FilePath path(gurl.path());
+#endif
+ if (net::GetMimeTypeFromFile(path, &temp_mime_type))
+ char_mime_type = temp_mime_type.c_str();
+ }
+
+ // Silverlight expects a valid mime type
+ DCHECK(strlen(char_mime_type) != 0);
+ NPError err = instance_->NPP_NewStream((NPMIMEType)char_mime_type,
+ &stream_, seekable_stream,
+ &requested_plugin_mode_);
+ if (err != NPERR_NO_ERROR) {
+ Notify(err);
+ return false;
+ }
+
+ opened_ = true;
+
+ if (requested_plugin_mode_ == NP_SEEK) {
+ seekable_stream_ = true;
+ }
+ // If the plugin has requested certain modes, then we need a copy
+ // of this file on disk. Open it and save it as we go.
+ if (requested_plugin_mode_ == NP_ASFILEONLY ||
+ requested_plugin_mode_ == NP_ASFILE) {
+ if (OpenTempFile() == false)
+ return false;
+ }
+
+ mime_type_ = char_mime_type;
+ return true;
+}
+
+int PluginStream::Write(const char *buffer, const int length,
+ int data_offset) {
+ // There may be two streams to write to - the plugin and the file.
+ // It is unclear what to do if we cannot write to both. The rules of
+ // this function are that the plugin must consume at least as many
+ // bytes as returned by the WriteReady call. So, we will attempt to
+ // write that many to both streams. If we can't write that many bytes
+ // to each stream, we'll return failure.
+
+ DCHECK(opened_);
+ if (WriteToFile(buffer, length) &&
+ WriteToPlugin(buffer, length, data_offset))
+ return length;
+
+ return -1;
+}
+
+bool PluginStream::WriteToFile(const char *buf, size_t length) {
+ // For ASFILEONLY, ASFILE, and SEEK modes, we need to write
+ // to the disk
+ if (TempFileIsValid() &&
+ (requested_plugin_mode_ == NP_ASFILE ||
+ requested_plugin_mode_ == NP_ASFILEONLY) ) {
+ size_t totalBytesWritten = 0, bytes;
+ do {
+ bytes = WriteBytes(buf, length);
+ totalBytesWritten += bytes;
+ } while (bytes > 0U && totalBytesWritten < length);
+
+ if (totalBytesWritten != length)
+ return false;
+ }
+
+ return true;
+}
+
+bool PluginStream::WriteToPlugin(const char *buf, const int length,
+ const int data_offset) {
+ // For NORMAL and ASFILE modes, we send the data to the plugin now
+ if (requested_plugin_mode_ != NP_NORMAL &&
+ requested_plugin_mode_ != NP_ASFILE &&
+ requested_plugin_mode_ != NP_SEEK)
+ return true;
+
+ int written = TryWriteToPlugin(buf, length, data_offset);
+ if (written == -1)
+ return false;
+
+ if (written < length) {
+ // Buffer the remaining data.
+ size_t remaining = length - written;
+ size_t previous_size = delivery_data_.size();
+ delivery_data_.resize(previous_size + remaining);
+ data_offset_ = data_offset;
+ memcpy(&delivery_data_[previous_size], buf + written, remaining);
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &PluginStream::OnDelayDelivery));
+ }
+
+ return true;
+}
+
+void PluginStream::OnDelayDelivery() {
+ // It is possible that the plugin stream may have closed before the task
+ // was hit.
+ if (!opened_) {
+ return;
+ }
+
+ int size = static_cast<int>(delivery_data_.size());
+ int written = TryWriteToPlugin(&delivery_data_.front(), size,
+ data_offset_);
+ if (written > 0) {
+ // Remove the data that we already wrote.
+ delivery_data_.erase(delivery_data_.begin(),
+ delivery_data_.begin() + written);
+ }
+}
+
+int PluginStream::TryWriteToPlugin(const char *buf, const int length,
+ const int data_offset) {
+ int byte_offset = 0;
+
+ if (data_offset > 0)
+ data_offset_ = data_offset;
+
+ while (byte_offset < length) {
+ int bytes_remaining = length - byte_offset;
+ int bytes_to_write = instance_->NPP_WriteReady(&stream_);
+ if (bytes_to_write > bytes_remaining)
+ bytes_to_write = bytes_remaining;
+
+ if (bytes_to_write == 0)
+ return byte_offset;
+
+ int bytes_consumed = instance_->NPP_Write(
+ &stream_, data_offset_, bytes_to_write,
+ const_cast<char*>(buf + byte_offset));
+ if (bytes_consumed < 0) {
+ // The plugin failed, which means that we need to close the stream.
+ Close(NPRES_NETWORK_ERR);
+ return -1;
+ }
+ if (bytes_consumed == 0) {
+ // The plugin couldn't take all of the data now.
+ return byte_offset;
+ }
+
+ // The plugin might report more that we gave it.
+ bytes_consumed = std::min(bytes_consumed, bytes_to_write);
+
+ data_offset_ += bytes_consumed;
+ byte_offset += bytes_consumed;
+ }
+
+ if (close_on_write_data_)
+ Close(NPRES_DONE);
+
+ return length;
+}
+
+bool PluginStream::Close(NPReason reason) {
+ if (opened_ == true) {
+ opened_ = false;
+
+ if (delivery_data_.size()) {
+ if (reason == NPRES_DONE) {
+ // There is more data to be streamed, don't destroy the stream now.
+ close_on_write_data_ = true;
+ return true;
+ } else {
+ // Stop any pending data from being streamed
+ delivery_data_.resize(0);
+ }
+ }
+
+ // If we have a temp file, be sure to close it.
+ // Also, allow the plugin to access it now.
+ if (TempFileIsValid()) {
+ CloseTempFile();
+ if (reason == NPRES_DONE)
+ WriteAsFile();
+ }
+
+ if (stream_.ndata != NULL) {
+ // Stream hasn't been closed yet.
+ NPError err = instance_->NPP_DestroyStream(&stream_, reason);
+ DCHECK(err == NPERR_NO_ERROR);
+ }
+ }
+
+ Notify(reason);
+ return true;
+}
+
+webkit_glue::WebPluginResourceClient* PluginStream::AsResourceClient() {
+ return NULL;
+}
+
+void PluginStream::Notify(NPReason reason) {
+ if (notify_needed_) {
+ instance_->NPP_URLNotify(stream_.url, reason, notify_data_);
+ notify_needed_ = false;
+ }
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_stream.h b/webkit/glue/plugins/plugin_stream.h
new file mode 100644
index 0000000..c5975b4
--- /dev/null
+++ b/webkit/glue/plugins/plugin_stream.h
@@ -0,0 +1,156 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_PLUGIN_STREAM_H_
+#define WEBKIT_GLUE_PLUGINS_PLUGIN_STREAM_H_
+
+#include "build/build_config.h"
+
+#include <string>
+#include <vector>
+
+#if defined(OS_POSIX)
+#include "base/file_path.h"
+#endif
+#include "base/ref_counted.h"
+#include "third_party/npapi/bindings/npapi.h"
+
+namespace webkit_glue {
+class WebPluginResourceClient;
+}
+
+namespace NPAPI {
+
+class PluginInstance;
+
+// Base class for a NPAPI stream. Tracks basic elements
+// of a stream for NPAPI notifications and stream position.
+class PluginStream : public base::RefCounted<PluginStream> {
+ public:
+ // Create a new PluginStream object. If needNotify is true, then the
+ // plugin will be notified when the stream has been fully sent.
+ PluginStream(PluginInstance *instance,
+ const char *url,
+ bool need_notify,
+ void *notify_data);
+
+ // In case of a redirect, this can be called to update the url. But it must
+ // be called before Open().
+ void UpdateUrl(const char* url);
+
+ // Opens the stream to the Plugin.
+ // If the mime-type is not specified, we'll try to find one based on the
+ // mime-types table and the extension (if any) in the URL.
+ // If the size of the stream is known, use length to set the size. If
+ // not known, set length to 0.
+ // The request_is_seekable parameter indicates whether byte range requests
+ // can be issued on the stream.
+ bool Open(const std::string &mime_type,
+ const std::string &headers,
+ uint32 length,
+ uint32 last_modified,
+ bool request_is_seekable);
+
+ // Writes to the stream.
+ int Write(const char *buf, const int len, int data_offset);
+
+ // Write the result as a file.
+ void WriteAsFile();
+
+ // Notify the plugin that a stream is complete.
+ void Notify(NPReason reason);
+
+ // Close the stream.
+ virtual bool Close(NPReason reason);
+
+ virtual webkit_glue::WebPluginResourceClient* AsResourceClient();
+
+ // Cancels any HTTP requests initiated by the stream.
+ virtual void CancelRequest() {}
+
+ const NPStream* stream() const { return &stream_; }
+
+ // setter/getter for the seekable attribute on the stream.
+ bool seekable() const { return seekable_stream_; }
+
+ void set_seekable(bool seekable) { seekable_stream_ = seekable; }
+
+ // getters for reading the notification related attributes on the stream.
+ bool notify_needed() const { return notify_needed_; }
+
+ void* notify_data() const { return notify_data_; }
+
+ std::string pending_redirect_url() const { return pending_redirect_url_; }
+
+ protected:
+ friend class base::RefCounted<PluginStream>;
+
+ virtual ~PluginStream();
+
+ PluginInstance* instance() { return instance_.get(); }
+ // Check if the stream is open.
+ bool open() { return opened_; }
+
+ // If the plugin participates in HTTP URL redirect handling then this member
+ // holds the url being redirected to while we wait for the plugin to make a
+ // decision on whether to allow or deny the redirect.
+ std::string pending_redirect_url_;
+
+ private:
+
+ // Open a temporary file for this stream.
+ // If successful, will set temp_file_name_, temp_file_handle_, and
+ // return true.
+ bool OpenTempFile();
+
+ // Closes the temporary file if it is open.
+ void CloseTempFile();
+
+ // Sends the data to the file. Called From WriteToFile.
+ size_t WriteBytes(const char *buf, size_t length);
+
+ // Sends the data to the file if it's open.
+ bool WriteToFile(const char *buf, size_t length);
+
+ // Sends the data to the plugin. If it's not ready, handles buffering it
+ // and retrying later.
+ bool WriteToPlugin(const char *buf, const int length, const int data_offset);
+
+ // Send the data to the plugin, returning how many bytes it accepted, or -1
+ // if an error occurred.
+ int TryWriteToPlugin(const char *buf, const int length,
+ const int data_offset);
+
+ // The callback which calls TryWriteToPlugin.
+ void OnDelayDelivery();
+
+ // Returns true if the temp file is valid and open for writing.
+ bool TempFileIsValid();
+
+ private:
+ NPStream stream_;
+ std::string headers_;
+ scoped_refptr<PluginInstance> instance_;
+ bool notify_needed_;
+ void * notify_data_;
+ bool close_on_write_data_;
+ uint16 requested_plugin_mode_;
+ bool opened_;
+#if defined(OS_WIN)
+ char temp_file_name_[MAX_PATH];
+ HANDLE temp_file_handle_;
+#elif defined(OS_POSIX)
+ FILE* temp_file_;
+ FilePath temp_file_path_;
+#endif
+ std::vector<char> delivery_data_;
+ int data_offset_;
+ bool seekable_stream_;
+ std::string mime_type_;
+ DISALLOW_COPY_AND_ASSIGN(PluginStream);
+};
+
+} // namespace NPAPI
+
+#endif // WEBKIT_GLUE_PLUGINS_PLUGIN_STREAM_H_
diff --git a/webkit/glue/plugins/plugin_stream_posix.cc b/webkit/glue/plugins/plugin_stream_posix.cc
new file mode 100644
index 0000000..d0e2291
--- /dev/null
+++ b/webkit/glue/plugins/plugin_stream_posix.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_stream.h"
+
+#include <string.h>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+
+namespace NPAPI {
+
+PluginStream::PluginStream(
+ PluginInstance *instance,
+ const char *url,
+ bool need_notify,
+ void *notify_data)
+ : instance_(instance),
+ notify_needed_(need_notify),
+ notify_data_(notify_data),
+ close_on_write_data_(false),
+ requested_plugin_mode_(NP_NORMAL),
+ opened_(false),
+ temp_file_(NULL),
+ temp_file_path_(),
+ data_offset_(0),
+ seekable_stream_(false) {
+ memset(&stream_, 0, sizeof(stream_));
+ stream_.url = strdup(url);
+}
+
+void PluginStream::UpdateUrl(const char* url) {
+ DCHECK(!opened_);
+ free(const_cast<char*>(stream_.url));
+ stream_.url = strdup(url);
+}
+
+void PluginStream::WriteAsFile() {
+ if (requested_plugin_mode_ == NP_ASFILE ||
+ requested_plugin_mode_ == NP_ASFILEONLY)
+ instance_->NPP_StreamAsFile(&stream_, temp_file_path_.value().c_str());
+}
+
+size_t PluginStream::WriteBytes(const char *buf, size_t length) {
+ return fwrite(buf, sizeof(char), length, temp_file_);
+}
+
+bool PluginStream::OpenTempFile() {
+ DCHECK(temp_file_ == NULL);
+
+ if (file_util::CreateTemporaryFile(&temp_file_path_))
+ temp_file_ = file_util::OpenFile(temp_file_path_, "a");
+
+ if (!temp_file_) {
+ temp_file_path_ = FilePath("");
+ return false;
+ }
+
+ return true;
+}
+
+void PluginStream::CloseTempFile() {
+ file_util::CloseFile(temp_file_);
+ temp_file_ = NULL;
+}
+
+bool PluginStream::TempFileIsValid() {
+ return temp_file_ != NULL;
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_stream_url.cc b/webkit/glue/plugins/plugin_stream_url.cc
new file mode 100644
index 0000000..7f9f355
--- /dev/null
+++ b/webkit/glue/plugins/plugin_stream_url.cc
@@ -0,0 +1,130 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_stream_url.h"
+
+#include "net/http/http_response_headers.h"
+#include "webkit/glue/plugins/plugin_host.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+#include "webkit/glue/plugins/webplugin.h"
+
+namespace NPAPI {
+
+PluginStreamUrl::PluginStreamUrl(
+ unsigned long resource_id,
+ const GURL &url,
+ PluginInstance *instance,
+ bool notify_needed,
+ void *notify_data)
+ : PluginStream(instance, url.spec().c_str(), notify_needed, notify_data),
+ url_(url),
+ id_(resource_id) {
+}
+
+PluginStreamUrl::~PluginStreamUrl() {
+ if (instance() && instance()->webplugin()) {
+ instance()->webplugin()->ResourceClientDeleted(AsResourceClient());
+ }
+}
+
+bool PluginStreamUrl::Close(NPReason reason) {
+ // Protect the stream against it being destroyed or the whole plugin instance
+ // being destroyed within the destroy stream handler.
+ scoped_refptr<PluginStream> protect(this);
+ CancelRequest();
+ bool result = PluginStream::Close(reason);
+ instance()->RemoveStream(this);
+ return result;
+}
+
+webkit_glue::WebPluginResourceClient* PluginStreamUrl::AsResourceClient() {
+ return static_cast<webkit_glue::WebPluginResourceClient*>(this);
+}
+
+void PluginStreamUrl::WillSendRequest(const GURL& url, int http_status_code) {
+ if (notify_needed()) {
+ // If the plugin participates in HTTP url redirect handling then notify it.
+ if (net::HttpResponseHeaders::IsRedirectResponseCode(http_status_code) &&
+ instance()->handles_url_redirects()) {
+ pending_redirect_url_ = url.spec();
+ instance()->NPP_URLRedirectNotify(url.spec().c_str(), http_status_code,
+ notify_data());
+ return;
+ }
+ }
+ url_ = url;
+ UpdateUrl(url.spec().c_str());
+}
+
+void PluginStreamUrl::DidReceiveResponse(const std::string& mime_type,
+ const std::string& headers,
+ uint32 expected_length,
+ uint32 last_modified,
+ bool request_is_seekable) {
+ // Protect the stream against it being destroyed or the whole plugin instance
+ // being destroyed within the new stream handler.
+ scoped_refptr<PluginStream> protect(this);
+
+ bool opened = Open(mime_type,
+ headers,
+ expected_length,
+ last_modified,
+ request_is_seekable);
+ if (!opened) {
+ CancelRequest();
+ instance()->RemoveStream(this);
+ } else {
+ if (id_ > 0)
+ instance()->webplugin()->SetDeferResourceLoading(id_, false);
+ }
+}
+
+void PluginStreamUrl::DidReceiveData(const char* buffer, int length,
+ int data_offset) {
+ if (!open())
+ return;
+
+ // Protect the stream against it being destroyed or the whole plugin instance
+ // being destroyed within the write handlers
+ scoped_refptr<PluginStream> protect(this);
+
+ if (length > 0) {
+ // The PluginStreamUrl instance could get deleted if the plugin fails to
+ // accept data in NPP_Write.
+ if (Write(const_cast<char*>(buffer), length, data_offset) > 0) {
+ if (id_ > 0)
+ instance()->webplugin()->SetDeferResourceLoading(id_, false);
+ }
+ }
+}
+
+void PluginStreamUrl::DidFinishLoading() {
+ if (!seekable()) {
+ Close(NPRES_DONE);
+ }
+}
+
+void PluginStreamUrl::DidFail() {
+ Close(NPRES_NETWORK_ERR);
+}
+
+bool PluginStreamUrl::IsMultiByteResponseExpected() {
+ return seekable();
+}
+
+int PluginStreamUrl::ResourceId() {
+ return id_;
+}
+
+void PluginStreamUrl::CancelRequest() {
+ if (id_ > 0) {
+ if (instance()->webplugin()) {
+ instance()->webplugin()->CancelResource(id_);
+ }
+ id_ = 0;
+ }
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_stream_url.h b/webkit/glue/plugins/plugin_stream_url.h
new file mode 100644
index 0000000..8642897
--- /dev/null
+++ b/webkit/glue/plugins/plugin_stream_url.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_URL_H__
+#define WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_URL_H__
+
+
+#include "webkit/glue/plugins/plugin_stream.h"
+#include "webkit/glue/plugins/webplugin.h"
+#include "googleurl/src/gurl.h"
+
+namespace NPAPI {
+
+class PluginInstance;
+
+// A NPAPI Stream based on a URL.
+class PluginStreamUrl : public PluginStream,
+ public webkit_glue::WebPluginResourceClient {
+ public:
+ // Create a new stream for sending to the plugin by fetching
+ // a URL. If notifyNeeded is set, then the plugin will be notified
+ // when the stream has been fully sent to the plugin. Initialize
+ // must be called before the object is used.
+ PluginStreamUrl(unsigned long resource_id,
+ const GURL &url,
+ PluginInstance *instance,
+ bool notify_needed,
+ void *notify_data);
+ virtual ~PluginStreamUrl();
+
+ // Stop sending the stream to the client.
+ // Overrides the base Close so we can cancel our fetching the URL if
+ // it is still loading.
+ virtual bool Close(NPReason reason);
+
+ virtual webkit_glue::WebPluginResourceClient* AsResourceClient();
+
+ virtual void CancelRequest();
+
+ //
+ // WebPluginResourceClient methods
+ //
+ virtual void WillSendRequest(const GURL& url, int http_status_code);
+ virtual void DidReceiveResponse(const std::string& mime_type,
+ const std::string& headers,
+ uint32 expected_length,
+ uint32 last_modified,
+ bool request_is_seekable);
+ virtual void DidReceiveData(const char* buffer, int length, int data_offset);
+ virtual void DidFinishLoading();
+ virtual void DidFail();
+ virtual bool IsMultiByteResponseExpected();
+ virtual int ResourceId();
+
+ private:
+ GURL url_;
+ unsigned long id_;
+
+ DISALLOW_COPY_AND_ASSIGN(PluginStreamUrl);
+};
+
+} // namespace NPAPI
+
+#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_STREAM_URL_H__
diff --git a/webkit/glue/plugins/plugin_stream_win.cc b/webkit/glue/plugins/plugin_stream_win.cc
new file mode 100644
index 0000000..0b6fcbd
--- /dev/null
+++ b/webkit/glue/plugins/plugin_stream_win.cc
@@ -0,0 +1,97 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_stream.h"
+
+#include "base/logging.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+
+namespace NPAPI {
+
+PluginStream::PluginStream(
+ PluginInstance *instance,
+ const char *url,
+ bool need_notify,
+ void *notify_data)
+ : instance_(instance),
+ notify_needed_(need_notify),
+ notify_data_(notify_data),
+ close_on_write_data_(false),
+ opened_(false),
+ requested_plugin_mode_(NP_NORMAL),
+ temp_file_handle_(INVALID_HANDLE_VALUE),
+ seekable_stream_(false),
+ data_offset_(0) {
+ memset(&stream_, 0, sizeof(stream_));
+ stream_.url = _strdup(url);
+ temp_file_name_[0] = '\0';
+}
+
+void PluginStream::UpdateUrl(const char* url) {
+ DCHECK(!opened_);
+ free(const_cast<char*>(stream_.url));
+ stream_.url = _strdup(url);
+ pending_redirect_url_.clear();
+}
+
+void PluginStream::WriteAsFile() {
+ if (requested_plugin_mode_ == NP_ASFILE ||
+ requested_plugin_mode_ == NP_ASFILEONLY)
+ instance_->NPP_StreamAsFile(&stream_, temp_file_name_);
+}
+
+size_t PluginStream::WriteBytes(const char *buf, size_t length) {
+ DWORD bytes;
+
+ if (!WriteFile(temp_file_handle_, buf, length, &bytes, 0))
+ return 0U;
+
+ return static_cast<size_t>(bytes);
+}
+
+bool PluginStream::OpenTempFile() {
+ DCHECK(temp_file_handle_ == INVALID_HANDLE_VALUE);
+
+ // The reason for using all the Ascii versions of these filesystem
+ // calls is that the filename which we pass back to the plugin
+ // via NPAPI is an ascii filename. Otherwise, we'd use wide-chars.
+ //
+ // TODO:
+ // This is a bug in NPAPI itself, and it needs to be fixed.
+ // The case which will fail is if a user has a multibyte name,
+ // but has the system locale set to english. GetTempPathA will
+ // return junk in this case, causing us to be unable to open the
+ // file.
+
+ char temp_directory[MAX_PATH];
+ if (GetTempPathA(MAX_PATH, temp_directory) == 0)
+ return false;
+ if (GetTempFileNameA(temp_directory, "npstream", 0, temp_file_name_) == 0)
+ return false;
+ temp_file_handle_ = CreateFileA(temp_file_name_,
+ FILE_ALL_ACCESS,
+ FILE_SHARE_READ,
+ 0,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ 0);
+ if (temp_file_handle_ == INVALID_HANDLE_VALUE) {
+ temp_file_name_[0] = '\0';
+ return false;
+ }
+ return true;
+}
+
+void PluginStream::CloseTempFile() {
+ if (temp_file_handle_ != INVALID_HANDLE_VALUE) {
+ CloseHandle(temp_file_handle_);
+ temp_file_handle_ = INVALID_HANDLE_VALUE;
+ }
+}
+
+bool PluginStream::TempFileIsValid() {
+ return temp_file_handle_ != INVALID_HANDLE_VALUE;
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_string_stream.cc b/webkit/glue/plugins/plugin_string_stream.cc
new file mode 100644
index 0000000..f174267
--- /dev/null
+++ b/webkit/glue/plugins/plugin_string_stream.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_string_stream.h"
+
+#include "googleurl/src/gurl.h"
+
+namespace NPAPI {
+
+PluginStringStream::PluginStringStream(
+ PluginInstance* instance,
+ const GURL& url,
+ bool notify_needed,
+ void* notify_data)
+ : PluginStream(instance, url.spec().c_str(), notify_needed, notify_data) {
+}
+
+PluginStringStream::~PluginStringStream() {
+}
+
+void PluginStringStream::SendToPlugin(const std::string &data,
+ const std::string &mime_type) {
+ // Protect the stream against it being destroyed or the whole plugin instance
+ // being destroyed within the plugin stream callbacks.
+ scoped_refptr<PluginStringStream> protect(this);
+
+ int length = static_cast<int>(data.length());
+ if (Open(mime_type, std::string(), length, 0, false)) {
+ // TODO - check if it was not fully sent, and figure out a backup plan.
+ int written = Write(data.c_str(), length, 0);
+ NPReason reason = written == length ? NPRES_DONE : NPRES_NETWORK_ERR;
+ Close(reason);
+ }
+}
+
+}
diff --git a/webkit/glue/plugins/plugin_string_stream.h b/webkit/glue/plugins/plugin_string_stream.h
new file mode 100644
index 0000000..68db2bf
--- /dev/null
+++ b/webkit/glue/plugins/plugin_string_stream.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_STRING_STREAM_H_
+#define WEBKIT_GLUE_PLUGIN_PLUGIN_STRING_STREAM_H_
+
+#include "webkit/glue/plugins/plugin_stream.h"
+
+class GURL;
+
+namespace NPAPI {
+
+class PluginInstance;
+
+// An NPAPI stream from a string.
+class PluginStringStream : public PluginStream {
+ public:
+ // Create a new stream for sending to the plugin.
+ // If notify_needed, will notify the plugin after the data has
+ // all been sent.
+ PluginStringStream(PluginInstance* instance,
+ const GURL& url,
+ bool notify_needed,
+ void* notify_data);
+
+ // Initiates the sending of data to the plugin.
+ void SendToPlugin(const std::string& data,
+ const std::string& mime_type);
+
+ private:
+ virtual ~PluginStringStream();
+
+ DISALLOW_COPY_AND_ASSIGN(PluginStringStream);
+};
+
+} // namespace NPAPI
+
+#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_STRING_STREAM_H_
diff --git a/webkit/glue/plugins/plugin_stubs.cc b/webkit/glue/plugins/plugin_stubs.cc
new file mode 100644
index 0000000..f8210c30
--- /dev/null
+++ b/webkit/glue/plugins/plugin_stubs.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file stubs out some functions needed to make the linker happy
+// without linking in all the plugin code. It should be removed once
+// we have plugins working on all platforms.
+
+// TODO(port): remove this file.
+
+#include "base/logging.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/plugin_stream.h"
+
+namespace NPAPI {
+
+PluginStream::~PluginStream() {
+ NOTIMPLEMENTED();
+}
+
+bool PluginStream::Close(NPReason reason) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+void PluginInstance::NPP_StreamAsFile(NPStream*, const char*) {
+ NOTIMPLEMENTED();
+}
+
+} // namespace NPAPI
diff --git a/webkit/glue/plugins/plugin_web_event_converter_mac.h b/webkit/glue/plugins/plugin_web_event_converter_mac.h
new file mode 100644
index 0000000..ec5b86f
--- /dev/null
+++ b/webkit/glue/plugins/plugin_web_event_converter_mac.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_WEB_EVENT_CONVERTER_MAC_H_
+#define WEBKIT_GLUE_PLUGIN_PLUGIN_WEB_EVENT_CONVERTER_MAC_H_
+
+#include "third_party/npapi/bindings/npapi.h"
+
+namespace WebKit {
+class WebInputEvent;
+class WebKeyboardEvent;
+class WebMouseEvent;
+class WebMouseWheelEvent;
+}
+
+// Utility class to translating WebInputEvent structs to equivalent structures
+// suitable for sending to Mac plugins (via NPP_HandleEvent).
+class PluginWebEventConverter {
+ public:
+ PluginWebEventConverter() {}
+ virtual ~PluginWebEventConverter() {}
+
+ // Initializes a converter for the given web event. Returns false if the event
+ // could not be converted.
+ virtual bool InitWithEvent(const WebKit::WebInputEvent& web_event);
+
+ // Returns a pointer to a plugin event--suitable for passing to
+ // NPP_HandleEvent--corresponding to the the web event this converter was
+ // created with. The pointer is valid only as long as this object is.
+ // Returns NULL iff InitWithEvent returned false.
+ virtual void* plugin_event() = 0;
+
+protected:
+ // To be overridden by subclasses to store a converted plugin representation
+ // of the given web event, suitable for returning from plugin_event.
+ // Returns true if the event was successfully converted.
+ virtual bool ConvertKeyboardEvent(
+ const WebKit::WebKeyboardEvent& web_event) = 0;
+ virtual bool ConvertMouseEvent(const WebKit::WebMouseEvent& web_event) = 0;
+ virtual bool ConvertMouseWheelEvent(
+ const WebKit::WebMouseWheelEvent& web_event) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PluginWebEventConverter);
+};
+
+// Factory for generating PluginWebEventConverter objects by event model.
+class PluginWebEventConverterFactory {
+ public:
+ // Returns a new PluginWebEventConverter corresponding to the given plugin
+ // event model.
+ static PluginWebEventConverter*
+ CreateConverterForModel(NPEventModel event_model);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PluginWebEventConverterFactory);
+};
+
+#endif // WEBKIT_GLUE_PLUGIN_PLUGIN_WEB_EVENT_CONVERTER_MAC_H_
diff --git a/webkit/glue/plugins/plugin_web_event_converter_mac.mm b/webkit/glue/plugins/plugin_web_event_converter_mac.mm
new file mode 100644
index 0000000..12d5cc6
--- /dev/null
+++ b/webkit/glue/plugins/plugin_web_event_converter_mac.mm
@@ -0,0 +1,359 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/logging.h"
+#include "webkit/glue/plugins/plugin_web_event_converter_mac.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
+
+using WebKit::WebInputEvent;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebMouseEvent;
+using WebKit::WebMouseWheelEvent;
+
+namespace {
+
+// Returns true if the caps lock flag should be set for the given event.
+bool CapsLockIsActive(const WebInputEvent& event) {
+ // Only key events have accurate information for the caps lock flag; see
+ // <https://bugs.webkit.org/show_bug.cgi?id=46518>.
+ // For other types, use the live state.
+ if (WebInputEvent::isKeyboardEventType(event.type))
+ return (event.modifiers & WebInputEvent::CapsLockOn) != 0;
+ else
+ return ([[NSApp currentEvent] modifierFlags] & NSAlphaShiftKeyMask) != 0;
+}
+
+} // namespace
+
+#pragma mark -
+
+#ifndef NP_NO_CARBON
+
+// Converter implementation for the Carbon event model.
+class CarbonPluginWebEventConverter : public PluginWebEventConverter {
+ public:
+ CarbonPluginWebEventConverter() {}
+ virtual ~CarbonPluginWebEventConverter() {}
+
+ virtual bool InitWithEvent(const WebInputEvent& web_event);
+
+ virtual void* plugin_event() { return &carbon_event_; }
+
+ protected:
+ virtual bool ConvertKeyboardEvent(const WebKeyboardEvent& key_event);
+ virtual bool ConvertMouseEvent(const WebMouseEvent& mouse_event);
+ virtual bool ConvertMouseWheelEvent(const WebMouseWheelEvent& wheel_event);
+
+ private:
+ // Returns the Carbon translation of web_event's modifiers.
+ static EventModifiers CarbonModifiers(const WebInputEvent& web_event);
+
+ NPEvent carbon_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(CarbonPluginWebEventConverter);
+};
+
+bool CarbonPluginWebEventConverter::InitWithEvent(
+ const WebInputEvent& web_event) {
+ memset(&carbon_event_, 0, sizeof(carbon_event_));
+ // Set the fields common to all event types.
+ carbon_event_.when = TickCount();
+ carbon_event_.modifiers |= CarbonModifiers(web_event);
+
+ return PluginWebEventConverter::InitWithEvent(web_event);
+}
+
+bool CarbonPluginWebEventConverter::ConvertKeyboardEvent(
+ const WebKeyboardEvent& key_event) {
+ // TODO: Figure out how to handle Unicode input to plugins, if that's
+ // even possible in the NPAPI Carbon event model.
+ carbon_event_.message = (key_event.nativeKeyCode << 8) & keyCodeMask;
+ carbon_event_.message |= key_event.text[0] & charCodeMask;
+ carbon_event_.modifiers |= btnState;
+
+ switch (key_event.type) {
+ case WebInputEvent::KeyDown:
+ if (key_event.modifiers & WebInputEvent::IsAutoRepeat)
+ carbon_event_.what = autoKey;
+ else
+ carbon_event_.what = keyDown;
+ return true;
+ case WebInputEvent::KeyUp:
+ carbon_event_.what = keyUp;
+ return true;
+ case WebInputEvent::RawKeyDown:
+ case WebInputEvent::Char:
+ // May be used eventually for IME, but currently not needed.
+ return false;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool CarbonPluginWebEventConverter::ConvertMouseEvent(
+ const WebMouseEvent& mouse_event) {
+ carbon_event_.where.h = mouse_event.globalX;
+ carbon_event_.where.v = mouse_event.globalY;
+
+ // Default to "button up"; override this for mouse down events below.
+ carbon_event_.modifiers |= btnState;
+
+ switch (mouse_event.button) {
+ case WebMouseEvent::ButtonLeft:
+ break;
+ case WebMouseEvent::ButtonMiddle:
+ carbon_event_.modifiers |= cmdKey;
+ break;
+ case WebMouseEvent::ButtonRight:
+ carbon_event_.modifiers |= controlKey;
+ break;
+ default:
+ NOTIMPLEMENTED();
+ }
+ switch (mouse_event.type) {
+ case WebInputEvent::MouseMove:
+ carbon_event_.what = nullEvent;
+ return true;
+ case WebInputEvent::MouseLeave:
+ case WebInputEvent::MouseEnter:
+ carbon_event_.what = NPEventType_AdjustCursorEvent;
+ return true;
+ case WebInputEvent::MouseDown:
+ carbon_event_.modifiers &= ~btnState;
+ carbon_event_.what = mouseDown;
+ return true;
+ case WebInputEvent::MouseUp:
+ carbon_event_.what = mouseUp;
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool CarbonPluginWebEventConverter::ConvertMouseWheelEvent(
+ const WebMouseWheelEvent& wheel_event) {
+ return false; // The Carbon NPAPI event model has no "mouse wheel" concept.
+}
+
+EventModifiers CarbonPluginWebEventConverter::CarbonModifiers(
+ const WebInputEvent& web_event) {
+ NSInteger modifiers = 0;
+ if (web_event.modifiers & WebInputEvent::ControlKey)
+ modifiers |= controlKey;
+ if (web_event.modifiers & WebInputEvent::ShiftKey)
+ modifiers |= shiftKey;
+ if (web_event.modifiers & WebInputEvent::AltKey)
+ modifiers |= optionKey;
+ if (web_event.modifiers & WebInputEvent::MetaKey)
+ modifiers |= cmdKey;
+ if (CapsLockIsActive(web_event))
+ modifiers |= alphaLock;
+ return modifiers;
+}
+
+#endif // !NP_NO_CARBON
+
+#pragma mark -
+
+// Converter implementation for the Cocoa event model.
+class CocoaPluginWebEventConverter : public PluginWebEventConverter {
+public:
+ CocoaPluginWebEventConverter() {}
+ virtual ~CocoaPluginWebEventConverter() {}
+
+ virtual bool InitWithEvent(const WebInputEvent& web_event);
+
+ virtual void* plugin_event() { return &cocoa_event_; }
+
+protected:
+ virtual bool ConvertKeyboardEvent(const WebKeyboardEvent& key_event);
+ virtual bool ConvertMouseEvent(const WebMouseEvent& mouse_event);
+ virtual bool ConvertMouseWheelEvent(const WebMouseWheelEvent& wheel_event);
+
+private:
+ // Returns the Cocoa translation of web_event's modifiers.
+ static NSUInteger CocoaModifiers(const WebInputEvent& web_event);
+
+ // Returns true if the given key is a modifier key.
+ static bool KeyIsModifier(int native_key_code);
+
+ NPCocoaEvent cocoa_event_;
+
+ DISALLOW_COPY_AND_ASSIGN(CocoaPluginWebEventConverter);
+};
+
+bool CocoaPluginWebEventConverter::InitWithEvent(
+ const WebInputEvent& web_event) {
+ memset(&cocoa_event_, 0, sizeof(cocoa_event_));
+ return PluginWebEventConverter::InitWithEvent(web_event);
+}
+
+bool CocoaPluginWebEventConverter::ConvertKeyboardEvent(
+ const WebKeyboardEvent& key_event) {
+ cocoa_event_.data.key.keyCode = key_event.nativeKeyCode;
+
+ cocoa_event_.data.key.modifierFlags |= CocoaModifiers(key_event);
+
+ // Modifier keys have their own event type, and don't get character or
+ // repeat data.
+ if (KeyIsModifier(key_event.nativeKeyCode)) {
+ cocoa_event_.type = NPCocoaEventFlagsChanged;
+ return true;
+ }
+
+ cocoa_event_.data.key.characters = reinterpret_cast<NPNSString*>(
+ [NSString stringWithFormat:@"%S", key_event.text]);
+ cocoa_event_.data.key.charactersIgnoringModifiers =
+ reinterpret_cast<NPNSString*>(
+ [NSString stringWithFormat:@"%S", key_event.unmodifiedText]);
+
+ if (key_event.modifiers & WebInputEvent::IsAutoRepeat)
+ cocoa_event_.data.key.isARepeat = true;
+
+ switch (key_event.type) {
+ case WebInputEvent::KeyDown:
+ cocoa_event_.type = NPCocoaEventKeyDown;
+ return true;
+ case WebInputEvent::KeyUp:
+ cocoa_event_.type = NPCocoaEventKeyUp;
+ return true;
+ case WebInputEvent::RawKeyDown:
+ case WebInputEvent::Char:
+ // May be used eventually for IME, but currently not needed.
+ return false;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool CocoaPluginWebEventConverter::ConvertMouseEvent(
+ const WebMouseEvent& mouse_event) {
+ cocoa_event_.data.mouse.pluginX = mouse_event.x;
+ cocoa_event_.data.mouse.pluginY = mouse_event.y;
+ cocoa_event_.data.mouse.modifierFlags |= CocoaModifiers(mouse_event);
+ cocoa_event_.data.mouse.clickCount = mouse_event.clickCount;
+ switch (mouse_event.button) {
+ case WebMouseEvent::ButtonLeft:
+ cocoa_event_.data.mouse.buttonNumber = 0;
+ break;
+ case WebMouseEvent::ButtonMiddle:
+ cocoa_event_.data.mouse.buttonNumber = 2;
+ break;
+ case WebMouseEvent::ButtonRight:
+ cocoa_event_.data.mouse.buttonNumber = 1;
+ break;
+ default:
+ cocoa_event_.data.mouse.buttonNumber = mouse_event.button;
+ break;
+ }
+ switch (mouse_event.type) {
+ case WebInputEvent::MouseDown:
+ cocoa_event_.type = NPCocoaEventMouseDown;
+ return true;
+ case WebInputEvent::MouseUp:
+ cocoa_event_.type = NPCocoaEventMouseUp;
+ return true;
+ case WebInputEvent::MouseMove: {
+ bool mouse_is_down =
+ (mouse_event.modifiers & WebInputEvent::LeftButtonDown) ||
+ (mouse_event.modifiers & WebInputEvent::RightButtonDown) ||
+ (mouse_event.modifiers & WebInputEvent::MiddleButtonDown);
+ cocoa_event_.type = mouse_is_down ? NPCocoaEventMouseDragged
+ : NPCocoaEventMouseMoved;
+ return true;
+ }
+ case WebInputEvent::MouseEnter:
+ cocoa_event_.type = NPCocoaEventMouseEntered;
+ return true;
+ case WebInputEvent::MouseLeave:
+ cocoa_event_.type = NPCocoaEventMouseExited;
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool CocoaPluginWebEventConverter::ConvertMouseWheelEvent(
+ const WebMouseWheelEvent& wheel_event) {
+ cocoa_event_.type = NPCocoaEventScrollWheel;
+ cocoa_event_.data.mouse.pluginX = wheel_event.x;
+ cocoa_event_.data.mouse.pluginY = wheel_event.y;
+ cocoa_event_.data.mouse.modifierFlags |= CocoaModifiers(wheel_event);
+ cocoa_event_.data.mouse.deltaX = wheel_event.deltaX;
+ cocoa_event_.data.mouse.deltaY = wheel_event.deltaY;
+ return true;
+}
+
+NSUInteger CocoaPluginWebEventConverter::CocoaModifiers(
+ const WebInputEvent& web_event) {
+ NSInteger modifiers = 0;
+ if (web_event.modifiers & WebInputEvent::ControlKey)
+ modifiers |= NSControlKeyMask;
+ if (web_event.modifiers & WebInputEvent::ShiftKey)
+ modifiers |= NSShiftKeyMask;
+ if (web_event.modifiers & WebInputEvent::AltKey)
+ modifiers |= NSAlternateKeyMask;
+ if (web_event.modifiers & WebInputEvent::MetaKey)
+ modifiers |= NSCommandKeyMask;
+ if (CapsLockIsActive(web_event))
+ modifiers |= NSAlphaShiftKeyMask;
+ return modifiers;
+}
+
+bool CocoaPluginWebEventConverter::KeyIsModifier(int native_key_code) {
+ switch (native_key_code) {
+ case 55: // Left command
+ case 54: // Right command
+ case 58: // Left option
+ case 61: // Right option
+ case 59: // Left control
+ case 62: // Right control
+ case 56: // Left shift
+ case 60: // Right shift
+ case 57: // Caps lock
+ return true;
+ default:
+ return false;
+ }
+}
+
+#pragma mark -
+
+bool PluginWebEventConverter::InitWithEvent(const WebInputEvent& web_event) {
+ if (web_event.type == WebInputEvent::MouseWheel) {
+ return ConvertMouseWheelEvent(
+ *static_cast<const WebMouseWheelEvent*>(&web_event));
+ } else if (WebInputEvent::isMouseEventType(web_event.type)) {
+ return ConvertMouseEvent(*static_cast<const WebMouseEvent*>(&web_event));
+ } else if (WebInputEvent::isKeyboardEventType(web_event.type)) {
+ return ConvertKeyboardEvent(
+ *static_cast<const WebKeyboardEvent*>(&web_event));
+ }
+ DLOG(WARNING) << "Unknown event type " << web_event.type;
+ return false;
+}
+
+#pragma mark -
+
+PluginWebEventConverter*
+ PluginWebEventConverterFactory::CreateConverterForModel(
+ NPEventModel event_model) {
+ switch (event_model) {
+ case NPEventModelCocoa:
+ return new CocoaPluginWebEventConverter();
+#ifndef NP_NO_CARBON
+ case NPEventModelCarbon:
+ return new CarbonPluginWebEventConverter();
+#endif
+ default:
+ NOTIMPLEMENTED();
+ return NULL;
+ }
+}
diff --git a/webkit/glue/plugins/quickdraw_drawing_manager_mac.cc b/webkit/glue/plugins/quickdraw_drawing_manager_mac.cc
new file mode 100644
index 0000000..424cc1e
--- /dev/null
+++ b/webkit/glue/plugins/quickdraw_drawing_manager_mac.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NP_NO_QUICKDRAW
+
+#include "webkit/glue/plugins/quickdraw_drawing_manager_mac.h"
+
+#include "webkit/glue/plugins/coregraphics_private_symbols_mac.h"
+
+// Turn off GCC warnings about deprecated functions (since QuickDraw is a
+// deprecated API). According to the GCC documentation, this can only be done
+// per file, not pushed and popped like some options can be.
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+QuickDrawDrawingManager::QuickDrawDrawingManager()
+ : plugin_window_(NULL), target_context_(NULL), fast_path_enabled_(false),
+ current_port_(NULL), target_world_(NULL), plugin_world_(NULL) {}
+
+QuickDrawDrawingManager::~QuickDrawDrawingManager() {
+ DestroyGWorlds();
+}
+
+void QuickDrawDrawingManager::SetFastPathEnabled(bool enabled) {
+ if (fast_path_enabled_ == enabled)
+ return;
+
+ fast_path_enabled_ = enabled;
+ if (enabled) {
+ if (!target_world_)
+ UpdateGWorlds();
+ // Copy our last window snapshot into our new source, since the plugin
+ // may not repaint everything.
+ CopyGWorldBits(target_world_, plugin_world_, plugin_size_);
+ current_port_ = plugin_world_;
+ } else {
+ current_port_ = GetWindowPort(plugin_window_);
+ }
+}
+
+void QuickDrawDrawingManager::SetTargetContext(CGContextRef context,
+ const gfx::Size& plugin_size) {
+ target_context_ = context;
+ if (plugin_size != plugin_size_) {
+ plugin_size_ = plugin_size;
+ // Pitch the old GWorlds, since they are the wrong size now.
+ DestroyGWorlds();
+ if (fast_path_enabled_)
+ UpdateGWorlds();
+ }
+}
+
+void QuickDrawDrawingManager::SetPluginWindow(WindowRef window) {
+ plugin_window_ = window;
+ if (!fast_path_enabled_)
+ current_port_ = GetWindowPort(window);
+}
+
+void QuickDrawDrawingManager::UpdateContext() {
+ if (fast_path_enabled_)
+ CopyGWorldBits(plugin_world_, target_world_, plugin_size_);
+ else
+ ScrapeWindow(plugin_window_, target_context_, plugin_size_);
+}
+
+bool QuickDrawDrawingManager::IsFastPathEnabled() {
+ return fast_path_enabled_;
+}
+
+void QuickDrawDrawingManager::MakePortCurrent() {
+ if (fast_path_enabled_)
+ SetGWorld(current_port_, NULL);
+ else
+ SetPort(current_port_);
+}
+
+void QuickDrawDrawingManager::DestroyGWorlds() {
+ if (plugin_world_) {
+ DisposeGWorld(plugin_world_);
+ plugin_world_ = NULL;
+ }
+ if (target_world_) {
+ DisposeGWorld(target_world_);
+ target_world_ = NULL;
+ }
+}
+
+void QuickDrawDrawingManager::UpdateGWorlds() {
+ DestroyGWorlds();
+ if (!target_context_)
+ return;
+
+ Rect window_bounds = {
+ 0, 0, plugin_size_.height(), plugin_size_.width()
+ };
+ // Create a GWorld pointing at the same bits as our target context.
+ if (target_context_) {
+ NewGWorldFromPtr(
+ &target_world_, k32BGRAPixelFormat, &window_bounds, NULL, NULL, 0,
+ static_cast<Ptr>(CGBitmapContextGetData(target_context_)),
+ static_cast<SInt32>(CGBitmapContextGetBytesPerRow(target_context_)));
+ }
+ // Create a GWorld for the plugin to paint into whenever it wants; since
+ // QuickDraw plugins don't draw at known times, they can't be allowed to draw
+ // directly into the shared memory.
+ NewGWorld(&plugin_world_, k32ARGBPixelFormat, &window_bounds,
+ NULL, NULL, kNativeEndianPixMap);
+ if (fast_path_enabled_)
+ current_port_ = plugin_world_;
+}
+
+void QuickDrawDrawingManager::ScrapeWindow(WindowRef window,
+ CGContextRef target_context,
+ const gfx::Size& plugin_size) {
+ if (!target_context)
+ return;
+
+ CGRect window_bounds = CGRectMake(0, 0,
+ plugin_size.width(),
+ plugin_size.height());
+ CGWindowID window_id = HIWindowGetCGWindowID(window);
+ CGContextSaveGState(target_context);
+ CGContextTranslateCTM(target_context, 0, plugin_size.height());
+ CGContextScaleCTM(target_context, 1.0, -1.0);
+ CGContextCopyWindowCaptureContentsToRect(target_context, window_bounds,
+ _CGSDefaultConnection(),
+ window_id, 0);
+ CGContextRestoreGState(target_context);
+}
+
+void QuickDrawDrawingManager::CopyGWorldBits(GWorldPtr source, GWorldPtr dest,
+ const gfx::Size& plugin_size) {
+ if (!(source && dest))
+ return;
+
+ Rect window_bounds = { 0, 0, plugin_size.height(), plugin_size.width() };
+ PixMapHandle source_pixmap = GetGWorldPixMap(source);
+ if (LockPixels(source_pixmap)) {
+ PixMapHandle dest_pixmap = GetGWorldPixMap(dest);
+ if (LockPixels(dest_pixmap)) {
+ SetGWorld(dest, NULL);
+ // Set foreground and background colors to avoid "colorizing" the image.
+ ForeColor(blackColor);
+ BackColor(whiteColor);
+ CopyBits(reinterpret_cast<BitMap*>(*source_pixmap),
+ reinterpret_cast<BitMap*>(*dest_pixmap),
+ &window_bounds, &window_bounds, srcCopy, NULL);
+ UnlockPixels(dest_pixmap);
+ }
+ UnlockPixels(source_pixmap);
+ }
+}
+
+#endif // !NP_NO_QUICKDRAW
diff --git a/webkit/glue/plugins/quickdraw_drawing_manager_mac.h b/webkit/glue/plugins/quickdraw_drawing_manager_mac.h
new file mode 100644
index 0000000..8163f92
--- /dev/null
+++ b/webkit/glue/plugins/quickdraw_drawing_manager_mac.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_QUICKDRAW_DRAWING_MANAGER_MAC_H_
+#define WEBKIT_GLUE_QUICKDRAW_DRAWING_MANAGER_MAC_H_
+
+#ifndef NP_NO_QUICKDRAW
+
+#import <Carbon/Carbon.h>
+
+#include "gfx/rect.h"
+
+// Plugin helper class encapsulating the details of capturing what a QuickDraw
+// drawing model plugin draws, then drawing it into a CGContext.
+class QuickDrawDrawingManager {
+ public:
+ QuickDrawDrawingManager();
+ ~QuickDrawDrawingManager();
+
+ // Sets the mode used for plugin drawing. If enabled is true the plugin draws
+ // into a GWorld that's not connected to a window, otherwise the plugin draws
+ // into our the plugin's dummy window (which is slower, since the call we use
+ // to scrape the window contents is much more expensive than copying between
+ // GWorlds).
+ void SetFastPathEnabled(bool enabled);
+
+ // Returns true if the fast path is currently enabled.
+ bool IsFastPathEnabled();
+
+ // Sets the context that the plugin bits should be copied into when
+ // UpdateContext is called. This object does not retain |context|, so the
+ // caller must call SetTargetContext again if the context changes.
+ // If the fast path is currently enabled, this call will cause the port to
+ // change.
+ void SetTargetContext(CGContextRef context, const gfx::Size& plugin_size);
+
+ // Sets the window that is used by the plugin. This object does not own the
+ // window, so the caler must call SetPluginWindow again if the window changes.
+ void SetPluginWindow(WindowRef window);
+
+ // Updates the target context with the current plugin bits.
+ void UpdateContext();
+
+ // Returns the port that the plugin should draw into. This returned port is
+ // only valid until the next call to SetFastPathEnabled (or SetTargetContext
+ // while the fast path is enabled).
+ CGrafPtr port() { return current_port_; }
+
+ // Makes the QuickDraw port current; should be called before calls where the
+ // plugin might draw.
+ void MakePortCurrent();
+
+ private:
+ // Updates the GWorlds used by the faster path.
+ void UpdateGWorlds();
+
+ // Deletes the GWorlds used by the faster path.
+ void DestroyGWorlds();
+
+ // Scrapes the contents of the window into the given context.
+ // Used for the slower path.
+ static void ScrapeWindow(WindowRef window, CGContextRef target_context,
+ const gfx::Size& plugin_size);
+
+ // Copies the source GWorld's bits into the target GWorld.
+ // Used for the faster path.
+ static void CopyGWorldBits(GWorldPtr source, GWorldPtr dest,
+ const gfx::Size& plugin_size);
+
+ WindowRef plugin_window_; // Weak reference.
+ CGContextRef target_context_; // Weak reference.
+ gfx::Size plugin_size_;
+ bool fast_path_enabled_;
+ CGrafPtr current_port_;
+ // Variables used for the faster path:
+ GWorldPtr target_world_; // Created lazily; may be NULL.
+ GWorldPtr plugin_world_; // Created lazily; may be NULL.
+};
+
+#endif // !NP_NO_QUICKDRAW
+
+#endif // QUICKDRAW_DRAWING_MANAGER_MAC
diff --git a/webkit/glue/plugins/test/Info.plist b/webkit/glue/plugins/test/Info.plist
new file mode 100644
index 0000000..37145fd
--- /dev/null
+++ b/webkit/glue/plugins/test/Info.plist
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>NPAPITestPlugIn</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.chromium.npapi_test_plugin</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>BRPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>CFPlugInDynamicRegisterFunction</key>
+ <string></string>
+ <key>CFPlugInDynamicRegistration</key>
+ <string>NO</string>
+ <key>WebPluginDescription</key>
+ <string>Simple NPAPI plug-in for Chromium unit tests</string>
+ <key>WebPluginMIMETypes</key>
+ <dict>
+ <key>application/vnd.npapi-test</key>
+ <dict>
+ <key>WebPluginExtensions</key>
+ <array>
+ <string>npapitest</string>
+ </array>
+ <key>WebPluginTypeDescription</key>
+ <string>test npapi</string>
+ </dict>
+ </dict>
+ <key>WebPluginName</key>
+ <string>Chromium NPAPI Test Plugin</string>
+</dict>
+</plist>
diff --git a/webkit/glue/plugins/test/npapi_constants.cc b/webkit/glue/plugins/test/npapi_constants.cc
new file mode 100644
index 0000000..75cc68f
--- /dev/null
+++ b/webkit/glue/plugins/test/npapi_constants.cc
@@ -0,0 +1,10 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/npapi_constants.h"
+
+namespace NPAPIClient {
+const char kTestCompleteCookie[] = "status";
+const char kTestCompleteSuccess[] = "OK";
+}
diff --git a/webkit/glue/plugins/test/npapi_constants.h b/webkit/glue/plugins/test/npapi_constants.h
new file mode 100644
index 0000000..6570c35
--- /dev/null
+++ b/webkit/glue/plugins/test/npapi_constants.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Constants for the NPAPI test
+
+#ifndef WEBKIT_PORT_PLUGINS_TEST_NPAPI_CONSTANTS_H__
+#define WEBKIT_PORT_PLUGINS_TEST_NPAPI_CONSTANTS_H__
+
+namespace NPAPIClient {
+// The name of the cookie which will be used to communicate between
+// the plugin and the test harness.
+extern const char kTestCompleteCookie[];
+
+// The cookie value which will be sent to the client upon successful
+// test.
+extern const char kTestCompleteSuccess[];
+}
+#endif // WEBKIT_PORT_PLUGINS_TEST_NPAPI_CONSTANTS_H__
diff --git a/webkit/glue/plugins/test/npapi_test.cc b/webkit/glue/plugins/test/npapi_test.cc
new file mode 100644
index 0000000..895a842
--- /dev/null
+++ b/webkit/glue/plugins/test/npapi_test.cc
@@ -0,0 +1,122 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//
+// npapitest
+//
+// This is a NPAPI Plugin Program which is used to test the Browser's NPAPI
+// host implementation. It is used in conjunction with the npapi_unittest.
+//
+// As a NPAPI Plugin, you can invoke it by creating a web page of the following
+// type:
+//
+// <embed src="content-to-load" type="application/vnd.npapi-test"
+// name="test-name">
+//
+// arguments:
+// src: This is the initial content which will be sent to the plugin.
+// type: Must be "application/vnd.npapi-test"
+// name: The testcase to run when invoked
+// id: The id of the test being run (for testing concurrent plugins)
+//
+// The Plugin drives the actual test, calling host functions and validating the
+// Host callbacks which it receives. It is the duty of the plugin to record
+// all errors.
+//
+// To indicate test completion, the plugin expects the containing HTML page to
+// implement two javascript functions:
+// onSuccess(string testname);
+// onFailure(string testname, string results);
+// The HTML host pages used in this test will then set a document cookie
+// which the automated test framework can poll for and discover that the
+// test has completed.
+//
+//
+// TESTS
+// When the PluginClient receives a NPP_New callback from the browser,
+// it looks at the "name" argument which is passed in. It verifies that
+// the name matches a known test, and instantiates that test. The test is
+// a subclass of PluginTest.
+//
+//
+
+#include "base/basictypes.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#if defined(__GNUC__) && __GNUC__ >= 4
+#define EXPORT __attribute__((visibility ("default")))
+#else
+#define EXPORT
+#endif
+
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+#if defined(OS_WIN)
+BOOL API_CALL DllMain(HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved) {
+ return TRUE;
+}
+#endif
+
+extern "C" {
+EXPORT NPError API_CALL NP_GetEntryPoints(NPPluginFuncs* pFuncs) {
+ return NPAPIClient::PluginClient::GetEntryPoints(pFuncs);
+}
+
+EXPORT NPError API_CALL NP_Shutdown() {
+ return NPAPIClient::PluginClient::Shutdown();
+}
+
+#if defined(OS_WIN) || defined(OS_MACOSX)
+EXPORT NPError API_CALL NP_Initialize(NPNetscapeFuncs* npnFuncs) {
+ return NPAPIClient::PluginClient::Initialize(npnFuncs);
+}
+#elif defined(OS_POSIX)
+EXPORT NPError API_CALL NP_Initialize(NPNetscapeFuncs* npnFuncs,
+ NPPluginFuncs* nppFuncs) {
+ NPError error = NPAPIClient::PluginClient::Initialize(npnFuncs);
+ if (error == NPERR_NO_ERROR) {
+ error = NP_GetEntryPoints(nppFuncs);
+ }
+ return error;
+}
+
+EXPORT NPError API_CALL NP_GetValue(NPP instance, NPPVariable variable,
+ void* value) {
+ NPError err = NPERR_NO_ERROR;
+
+ switch (variable) {
+ case NPPVpluginNameString:
+ *(static_cast<const char**>(value)) = "NPAPI Test Plugin";
+ break;
+ case NPPVpluginDescriptionString:
+ *(static_cast<const char**>(value)) =
+ "Simple NPAPI plug-in for Chromium unit tests";
+ break;
+ case NPPVpluginNeedsXEmbed:
+ *(static_cast<NPBool*>(value)) = true;
+ break;
+ default:
+ err = NPERR_GENERIC_ERROR;
+ break;
+ }
+
+ return err;
+}
+
+EXPORT const char* API_CALL NP_GetMIMEDescription(void) {
+ // The layout test LayoutTests/fast/js/navigator-mimeTypes-length.html
+ // asserts that the number of mimetypes handled by plugins should be
+ // greater than the number of plugins. We specify a mimetype here so
+ // this plugin has at least one.
+ return "application/vnd.npapi-test:npapitest:test npapi";
+}
+#endif // OS_POSIX
+} // extern "C"
+
+namespace WebCore {
+ const char* currentTextBreakLocaleID() { return "en_us"; }
+}
diff --git a/webkit/glue/plugins/test/npapi_test.def b/webkit/glue/plugins/test/npapi_test.def
new file mode 100644
index 0000000..4481c16
--- /dev/null
+++ b/webkit/glue/plugins/test/npapi_test.def
@@ -0,0 +1,6 @@
+LIBRARY npapi_test_plugin
+
+EXPORTS
+ NP_GetEntryPoints @1
+ NP_Initialize @2
+ NP_Shutdown @3
diff --git a/webkit/glue/plugins/test/npapi_test.rc b/webkit/glue/plugins/test/npapi_test.rc
new file mode 100644
index 0000000..524dda4
--- /dev/null
+++ b/webkit/glue/plugins/test/npapi_test.rc
@@ -0,0 +1,102 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "FileDescription", "NPAPI Test Plugin"
+ VALUE "FileVersion", "1, 0, 0, 1"
+ VALUE "InternalName", "npapi_test_plugin"
+ VALUE "LegalCopyright", "Copyright (C) 2007"
+ VALUE "MIMEType", "application/vnd.npapi-test"
+ VALUE "OriginalFilename", "npapi_test_plugin.dll"
+ VALUE "ProductName", "NPAPI Test Plugin"
+ VALUE "ProductVersion", "1, 0, 0, 1"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/webkit/glue/plugins/test/plugin_arguments_test.cc b/webkit/glue/plugins/test/plugin_arguments_test.cc
new file mode 100644
index 0000000..46ccf43
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_arguments_test.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+
+#include "webkit/glue/plugins/test/plugin_arguments_test.h"
+
+namespace NPAPIClient {
+
+PluginArgumentsTest::PluginArgumentsTest(NPP id,
+ NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions) {
+}
+
+NPError PluginArgumentsTest::New(uint16 mode, int16 argc,
+ const char* argn[], const char* argv[],
+ NPSavedData* saved) {
+ // mode: should be the string either "NP_EMBED" or "NP_FULL",
+ // depending on the mode passed in.
+ // count: the count of "val" arguments. If the value is
+ // 2, then we'll find arguments "val1" and "val2". If
+ // the value is 0, then there will be no "val" arguments.
+ // size: each val string will be this size * the value's
+ // index. E.g if size is "10", val1 will be 10bytes,
+ // and val2 will be 20bytes.
+ const char *mode_string = GetArgValue("mode", argc, argn, argv);
+ ExpectAsciiStringNotEqual(mode_string, (const char *)NULL);
+ if (mode_string != NULL) {
+ std::string mode_dep_string = mode_string;
+ if (mode == NP_EMBED)
+ ExpectStringLowerCaseEqual(mode_dep_string, "np_embed");
+ else if (mode == NP_FULL)
+ ExpectStringLowerCaseEqual(mode_dep_string, "np_full");
+ }
+
+ const char *count_string = GetArgValue("count", argc, argn, argv);
+ if (count_string != NULL) {
+ int max_args = atoi(count_string);
+
+ const char *size_string = GetArgValue("size", argc, argn, argv);
+ ExpectAsciiStringNotEqual(size_string, (const char *)NULL);
+ if (size_string != NULL) {
+ int size = atoi(size_string);
+
+ for (int index = 1; index <= max_args; index++) {
+ std::string arg_name = base::StringPrintf("%s%d", "val", index);
+ const char *val_string = GetArgValue(arg_name.c_str(), argc, argn,
+ argv);
+ ExpectAsciiStringNotEqual(val_string, (const char*)NULL);
+ if (val_string != NULL)
+ ExpectIntegerEqual((int)strlen(val_string), (index*size));
+ }
+ }
+ }
+
+ return PluginTest::New(mode, argc, argn, argv, saved);
+}
+
+NPError PluginArgumentsTest::SetWindow(NPWindow* pNPWindow) {
+ // This test just tests the arguments. We're done now.
+ this->SignalTestCompleted();
+
+ return NPERR_NO_ERROR;
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_arguments_test.h b/webkit/glue/plugins/test/plugin_arguments_test.h
new file mode 100644
index 0000000..aa05f19
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_arguments_test.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_PORT_PLUGINS_TEST_PLUGIN_ARGUMENTS_TEST_H__
+#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_ARGUMENTS_TEST_H__
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// The PluginArgumentsTest test that we properly receive arguments
+// intended for the plugin.
+//
+// This is basically overkill for testing that the arguments passed
+// to the plugin match what we expect.
+//
+// We expect to find the following arguments:
+// mode: should be the string either "NP_EMBED" or "NP_FULL",
+// depending on the mode passed in.
+// count: the count of "val" arguments. If the value is
+// 2, then we'll find arguments "val1" and "val2". If
+// the value is 0, then there will be no "val" arguments.
+// size: each val string will be this size * the value's
+// index. E.g if size is "10", val1 will be 10bytes,
+// and val2 will be 20bytes.
+//
+class PluginArgumentsTest : public PluginTest {
+ public:
+ // Constructor.
+ PluginArgumentsTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ // Initialize this PluginTest based on the arguments from NPP_New.
+ virtual NPError New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved);
+
+ // NPAPI SetWindow handler.
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_ARGUMENTS_TEST_H__
diff --git a/webkit/glue/plugins/test/plugin_client.cc b/webkit/glue/plugins/test/plugin_client.cc
new file mode 100644
index 0000000..8358340
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_client.cc
@@ -0,0 +1,240 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+#include "base/string_util.h"
+#include "webkit/glue/plugins/test/plugin_test.h"
+#include "webkit/glue/plugins/test/plugin_test_factory.h"
+
+namespace NPAPIClient {
+
+NPNetscapeFuncs* PluginClient::host_functions_;
+
+NPError PluginClient::GetEntryPoints(NPPluginFuncs* pFuncs) {
+ if (pFuncs == NULL)
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+
+ if (pFuncs->size < sizeof(NPPluginFuncs))
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+
+ pFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+ pFuncs->newp = NPP_New;
+ pFuncs->destroy = NPP_Destroy;
+ pFuncs->setwindow = NPP_SetWindow;
+ pFuncs->newstream = NPP_NewStream;
+ pFuncs->destroystream = NPP_DestroyStream;
+ pFuncs->asfile = NPP_StreamAsFile;
+ pFuncs->writeready = NPP_WriteReady;
+ pFuncs->write = NPP_Write;
+ pFuncs->print = NPP_Print;
+ pFuncs->event = NPP_HandleEvent;
+ pFuncs->urlnotify = NPP_URLNotify;
+ pFuncs->getvalue = NPP_GetValue;
+ pFuncs->setvalue = NPP_SetValue;
+ pFuncs->javaClass = NULL;
+ pFuncs->urlredirectnotify = NPP_URLRedirectNotify;
+
+ return NPERR_NO_ERROR;
+}
+
+NPError PluginClient::Initialize(NPNetscapeFuncs* pFuncs) {
+ if (pFuncs == NULL) {
+ return NPERR_INVALID_FUNCTABLE_ERROR;
+ }
+
+ if (static_cast<unsigned char>((pFuncs->version >> 8) & 0xff) >
+ NP_VERSION_MAJOR) {
+ return NPERR_INCOMPATIBLE_VERSION_ERROR;
+ }
+
+#if defined(OS_WIN)
+ // Check if we should crash.
+ HANDLE crash_event = CreateEvent(NULL, TRUE, FALSE, L"TestPluginCrashOnInit");
+ if (WaitForSingleObject(crash_event, 0) == WAIT_OBJECT_0) {
+ int *zero = NULL;
+ *zero = 0;
+ }
+ CloseHandle(crash_event);
+#endif
+
+ host_functions_ = pFuncs;
+
+ return NPERR_NO_ERROR;
+}
+
+NPError PluginClient::Shutdown() {
+ return NPERR_NO_ERROR;
+}
+
+} // namespace NPAPIClient
+
+extern "C" {
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode,
+ int16 argc, char* argn[], char* argv[], NPSavedData* saved) {
+ if (instance == NULL)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ // We look at the test name requested via the plugin arguments. We match
+ // that against a given test and try to instantiate it.
+
+ // lookup the name parameter
+ std::string test_name;
+ for (int name_index = 0; name_index < argc; name_index++) {
+ if (base::strcasecmp(argn[name_index], "name") == 0) {
+ test_name = argv[name_index];
+ break;
+ }
+ }
+ if (test_name.empty())
+ return NPERR_GENERIC_ERROR; // no name found
+
+ NPAPIClient::PluginTest* new_test = NPAPIClient::CreatePluginTest(test_name,
+ instance, NPAPIClient::PluginClient::HostFunctions());
+ if (new_test == NULL) {
+ // If we don't have a test case for this, create a
+ // generic one which basically never fails.
+ LOG(WARNING) << "Unknown test name '" << test_name
+ << "'; using default test.";
+ new_test = new NPAPIClient::PluginTest(instance,
+ NPAPIClient::PluginClient::HostFunctions());
+ }
+
+ NPError ret = new_test->New(mode, argc, (const char**)argn,
+ (const char**)argv, saved);
+ if ((ret == NPERR_NO_ERROR) && new_test->IsWindowless()) {
+ NPAPIClient::PluginClient::HostFunctions()->setvalue(
+ instance, NPPVpluginWindowBool, NULL);
+ }
+
+ return ret;
+}
+
+NPError NPP_Destroy(NPP instance, NPSavedData** save) {
+ if (instance == NULL)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ NPAPIClient::PluginTest* plugin =
+ reinterpret_cast<NPAPIClient::PluginTest*>(instance->pdata);
+
+ NPError rv = plugin->Destroy();
+ delete plugin;
+ return rv;
+}
+
+NPError NPP_SetWindow(NPP instance, NPWindow* pNPWindow) {
+ if (instance == NULL)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ NPAPIClient::PluginTest* plugin =
+ reinterpret_cast<NPAPIClient::PluginTest*>(instance->pdata);
+
+ return plugin->SetWindow(pNPWindow);
+}
+
+NPError NPP_NewStream(NPP instance, NPMIMEType type,
+ NPStream* stream, NPBool seekable, uint16* stype) {
+ if (instance == NULL)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ NPAPIClient::PluginTest* plugin =
+ reinterpret_cast<NPAPIClient::PluginTest*>(instance->pdata);
+
+ return plugin->NewStream(type, stream, seekable, stype);
+}
+
+int32 NPP_WriteReady(NPP instance, NPStream *stream) {
+ if (instance == NULL)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ NPAPIClient::PluginTest* plugin =
+ reinterpret_cast<NPAPIClient::PluginTest*>(instance->pdata);
+
+ return plugin->WriteReady(stream);
+}
+
+int32 NPP_Write(NPP instance, NPStream *stream, int32 offset,
+ int32 len, void *buffer) {
+ if (instance == NULL)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ NPAPIClient::PluginTest* plugin =
+ reinterpret_cast<NPAPIClient::PluginTest*>(instance->pdata);
+
+ return plugin->Write(stream, offset, len, buffer);
+}
+
+NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPError reason) {
+ if (instance == NULL)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ NPAPIClient::PluginTest* plugin =
+ reinterpret_cast<NPAPIClient::PluginTest*>(instance->pdata);
+
+ return plugin->DestroyStream(stream, reason);
+}
+
+void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname) {
+ if (instance == NULL)
+ return;
+
+ NPAPIClient::PluginTest* plugin =
+ reinterpret_cast<NPAPIClient::PluginTest*>(instance->pdata);
+
+ return plugin->StreamAsFile(stream, fname);
+}
+
+void NPP_Print(NPP instance, NPPrint* printInfo) {
+ if (instance == NULL)
+ return;
+
+ // XXXMB - do work here.
+}
+
+void NPP_URLNotify(NPP instance, const char* url, NPReason reason,
+ void* notifyData) {
+ if (instance == NULL)
+ return;
+
+ NPAPIClient::PluginTest* plugin =
+ reinterpret_cast<NPAPIClient::PluginTest*>(instance->pdata);
+
+ return plugin->URLNotify(url, reason, notifyData);
+}
+
+NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value) {
+ if (instance == NULL)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ // XXXMB - do work here.
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value) {
+ if (instance == NULL)
+ return NPERR_INVALID_INSTANCE_ERROR;
+
+ // XXXMB - do work here.
+ return NPERR_GENERIC_ERROR;
+}
+
+int16 NPP_HandleEvent(NPP instance, void* event) {
+ if (instance == NULL)
+ return 0;
+
+ NPAPIClient::PluginTest* plugin =
+ reinterpret_cast<NPAPIClient::PluginTest*>(instance->pdata);
+
+ return plugin->HandleEvent(event);
+}
+
+void NPP_URLRedirectNotify(NPP instance, const char* url, int32_t status,
+ void* notify_data) {
+ if (instance) {
+ NPAPIClient::PluginTest* plugin =
+ reinterpret_cast<NPAPIClient::PluginTest*>(instance->pdata);
+ plugin->URLRedirectNotify(url, status, notify_data);
+ }
+}
+} // extern "C"
diff --git a/webkit/glue/plugins/test/plugin_client.h b/webkit/glue/plugins/test/plugin_client.h
new file mode 100644
index 0000000..a6291b0
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_client.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_PORT_PLUGINS_TEST_PLUGIN_CLIENT_H__
+#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_CLIENT_H__
+
+#include "third_party/npapi/bindings/npapi.h"
+#include "third_party/npapi/bindings/nphostapi.h"
+
+namespace NPAPIClient {
+
+// A PluginClient is a NPAPI Plugin. This class contains
+// the bootstrapping functions used by the browser to load
+// the plugin.
+class PluginClient {
+ public:
+ // Although not documented in the NPAPI specification, this function
+ // gets the list of entry points in the NPAPI Plugin (client) for the
+ // NPAPI Host to call.
+ static NPError GetEntryPoints(NPPluginFuncs* pFuncs);
+
+ // The browser calls this function only once: when a plug-in is loaded,
+ // before the first instance is created. This is the first function that
+ // the browser calls. NP_Initialize tells the plug-in that the browser has
+ // loaded it and provides global initialization. Allocate any memory or
+ // resources shared by all instances of your plug-in at this time.
+ static NPError Initialize(NPNetscapeFuncs* pFuncs);
+
+ // The browser calls this function once after the last instance of your
+ // plug-in is destroyed, before unloading the plug-in library itself. Use
+ // NP_Shutdown to delete any data allocated in NP_Initialize to be shared
+ // by all instances of a plug-in.
+ static NPError Shutdown();
+
+ // The table of functions provided by the host.
+ static NPNetscapeFuncs *HostFunctions() { return host_functions_; }
+
+ private:
+ static NPNetscapeFuncs* host_functions_;
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_CLIENT_H__
diff --git a/webkit/glue/plugins/test/plugin_create_instance_in_paint.cc b/webkit/glue/plugins/test/plugin_create_instance_in_paint.cc
new file mode 100644
index 0000000..f98f89b
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_create_instance_in_paint.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_create_instance_in_paint.h"
+
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+namespace NPAPIClient {
+
+CreateInstanceInPaintTest::CreateInstanceInPaintTest(
+ NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ window_(NULL), created_(false) {
+}
+
+NPError CreateInstanceInPaintTest::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ if (test_id() == "1") {
+ if (!window_) {
+ static ATOM window_class = 0;
+ if (!window_class) {
+ WNDCLASSEX wcex;
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.style = CS_DBLCLKS;
+ wcex.lpfnWndProc =
+ &NPAPIClient::CreateInstanceInPaintTest::WindowProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = GetModuleHandle(NULL);
+ wcex.hIcon = 0;
+ wcex.hCursor = 0;
+ wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1);
+ wcex.lpszMenuName = 0;
+ wcex.lpszClassName = L"CreateInstanceInPaintTestWindowClass";
+ wcex.hIconSm = 0;
+ window_class = RegisterClassEx(&wcex);
+ }
+
+ HWND parent = reinterpret_cast<HWND>(pNPWindow->window);
+ window_ = CreateWindowEx(
+ WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR,
+ MAKEINTATOM(window_class), 0,
+ WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE ,
+ 0, 0, 100, 100, parent, 0, GetModuleHandle(NULL), 0);
+ DCHECK(window_);
+ // TODO: this property leaks.
+ ::SetProp(window_, L"Plugin_Instance", this);
+ }
+ } else if (test_id() == "2") {
+ SignalTestCompleted();
+ } else {
+ NOTREACHED();
+ }
+ return NPERR_NO_ERROR;
+}
+
+LRESULT CALLBACK CreateInstanceInPaintTest::WindowProc(
+ HWND window, UINT message, WPARAM wparam, LPARAM lparam) {
+ if (message == WM_PAINT) {
+ CreateInstanceInPaintTest* this_instance =
+ reinterpret_cast<CreateInstanceInPaintTest*>
+ (::GetProp(window, L"Plugin_Instance"));
+ if (this_instance->test_id() == "1" && !this_instance->created_) {
+ ::RemoveProp(window, L"Plugin_Instance");
+ this_instance->created_ = true;
+ this_instance->HostFunctions()->geturlnotify(
+ this_instance->id(), "javascript:CreateNewInstance()", NULL,
+ reinterpret_cast<void*>(1));
+ }
+ }
+
+ return DefWindowProc(window, message, wparam, lparam);
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_create_instance_in_paint.h b/webkit/glue/plugins/test/plugin_create_instance_in_paint.h
new file mode 100644
index 0000000..84d7a94
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_create_instance_in_paint.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_CREATE_INSTANCE_IN_PAINT_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_CREATE_INSTANCE_IN_PAINT_H
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class tests that creating a new plugin via script while handling a
+// Windows message doesn't cause a deadlock.
+class CreateInstanceInPaintTest : public PluginTest {
+ public:
+ // Constructor.
+ CreateInstanceInPaintTest(NPP id, NPNetscapeFuncs *host_functions);
+ //
+ // NPAPI functions
+ //
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+
+ private:
+ static LRESULT CALLBACK WindowProc(
+ HWND window, UINT message, WPARAM wparam, LPARAM lparam);
+
+ HWND window_;
+ bool created_;
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_CREATE_INSTANCE_IN_PAINT_H
diff --git a/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.cc b/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.cc
new file mode 100644
index 0000000..15318b4
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h"
+
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+namespace NPAPIClient {
+
+#define kUrl "javascript:window.location+\"\""
+#define kUrlStreamId 1
+
+DeletePluginInStreamTest::DeletePluginInStreamTest(NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ test_started_(false) {
+}
+
+NPError DeletePluginInStreamTest::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ if (!test_started_) {
+ std::string url = "self_delete_plugin_stream.html";
+ HostFunctions()->geturlnotify(id(), url.c_str(), NULL,
+ reinterpret_cast<void*>(kUrlStreamId));
+ test_started_ = true;
+ }
+ return NPERR_NO_ERROR;
+}
+
+NPError DeletePluginInStreamTest::NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype) {
+ NPIdentifier delete_id = HostFunctions()->getstringidentifier("DeletePluginWithinScript");
+
+ NPObject *window_obj = NULL;
+ HostFunctions()->getvalue(id(), NPNVWindowNPObject, &window_obj);
+
+ NPVariant rv;
+ HostFunctions()->invoke(id(), window_obj, delete_id, NULL, 0, &rv);
+
+ return NPERR_NO_ERROR;
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h b/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h
new file mode 100644
index 0000000..418e976
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_DELETE_PLUGIN_IN_STREAM_TEST_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_DELETE_PLUGIN_IN_STREAM_TEST_H
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class tests
+class DeletePluginInStreamTest : public PluginTest {
+ public:
+ // Constructor.
+ DeletePluginInStreamTest(NPP id, NPNetscapeFuncs *host_functions);
+ //
+ // NPAPI functions
+ //
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+ virtual NPError NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype);
+ private:
+ bool test_started_;
+ std::string self_url_;
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_DELETE_PLUGIN_IN_STREAM_TEST_H
diff --git a/webkit/glue/plugins/test/plugin_get_javascript_url2_test.cc b/webkit/glue/plugins/test/plugin_get_javascript_url2_test.cc
new file mode 100644
index 0000000..d17dced
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_get_javascript_url2_test.cc
@@ -0,0 +1,134 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_get_javascript_url2_test.h"
+
+#include "base/basictypes.h"
+
+// url for "self".
+#define SELF_URL "javascript:window.location+\"\""
+// The identifier for the self url stream.
+#define SELF_URL_STREAM_ID 1
+
+// The identifier for the fetched url stream.
+#define FETCHED_URL_STREAM_ID 2
+
+// The maximum chunk size of stream data.
+#define STREAM_CHUNK 197
+
+const int kNPNEvaluateTimerID = 100;
+const int kNPNEvaluateTimerElapse = 50;
+
+namespace NPAPIClient {
+
+ExecuteGetJavascriptUrl2Test::ExecuteGetJavascriptUrl2Test(
+ NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ test_started_(false) {
+}
+
+NPError ExecuteGetJavascriptUrl2Test::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ if (!test_started_) {
+ std::string url = SELF_URL;
+ HostFunctions()->geturlnotify(id(), url.c_str(), "_self",
+ reinterpret_cast<void*>(SELF_URL_STREAM_ID));
+ test_started_ = true;
+ }
+ return NPERR_NO_ERROR;
+}
+
+NPError ExecuteGetJavascriptUrl2Test::NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype) {
+ if (stream == NULL) {
+ SetError("NewStream got null stream");
+ return NPERR_INVALID_PARAM;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+ unsigned long stream_id = reinterpret_cast<unsigned long>(stream->notifyData);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+ return NPERR_NO_ERROR;
+}
+
+int32 ExecuteGetJavascriptUrl2Test::WriteReady(NPStream *stream) {
+ return STREAM_CHUNK;
+}
+
+int32 ExecuteGetJavascriptUrl2Test::Write(NPStream *stream, int32 offset, int32 len,
+ void *buffer) {
+ if (stream == NULL) {
+ SetError("Write got null stream");
+ return -1;
+ }
+ if (len < 0 || len > STREAM_CHUNK) {
+ SetError("Write got bogus stream chunk size");
+ return -1;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+ unsigned long stream_id = reinterpret_cast<unsigned long>(stream->notifyData);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ self_url_.append(static_cast<char*>(buffer), len);
+ break;
+ default:
+ SetError("Unexpected write callback");
+ break;
+ }
+ // Pretend that we took all the data.
+ return len;
+}
+
+
+NPError ExecuteGetJavascriptUrl2Test::DestroyStream(NPStream *stream, NPError reason) {
+ if (stream == NULL) {
+ SetError("NewStream got null stream");
+ return NPERR_INVALID_PARAM;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+ unsigned long stream_id = reinterpret_cast<unsigned long>(stream->notifyData);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ // don't care
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+ return NPERR_NO_ERROR;
+}
+
+void ExecuteGetJavascriptUrl2Test::URLNotify(const char* url, NPReason reason, void* data) {
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(data),
+ cast_validity_check);
+
+ unsigned long stream_id = reinterpret_cast<unsigned long>(data);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ if (strcmp(url, SELF_URL) != 0)
+ SetError("URLNotify reported incorrect url for SELF_URL");
+ if (self_url_.empty())
+ SetError("Failed to obtain window location.");
+ SignalTestCompleted();
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_get_javascript_url2_test.h b/webkit/glue/plugins/test/plugin_get_javascript_url2_test.h
new file mode 100644
index 0000000..557da76
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_get_javascript_url2_test.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_GET_JAVASCRIPT_URL2_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_GET_JAVASCRIPT_URL2_H
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class tests NPP_GetURLNotify for a javascript URL with _top
+// as the target frame.
+class ExecuteGetJavascriptUrl2Test : public PluginTest {
+ public:
+ // Constructor.
+ ExecuteGetJavascriptUrl2Test(NPP id, NPNetscapeFuncs *host_functions);
+
+ //
+ // NPAPI functions
+ //
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+ virtual NPError NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype);
+ virtual int32 WriteReady(NPStream *stream);
+ virtual int32 Write(NPStream *stream, int32 offset, int32 len,
+ void *buffer);
+ virtual NPError DestroyStream(NPStream *stream, NPError reason);
+ virtual void URLNotify(const char* url, NPReason reason, void* data);
+
+ private:
+ bool test_started_;
+ std::string self_url_;
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_GET_JAVASCRIPT_URL2_H
diff --git a/webkit/glue/plugins/test/plugin_get_javascript_url_test.cc b/webkit/glue/plugins/test/plugin_get_javascript_url_test.cc
new file mode 100644
index 0000000..50f5e5a
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_get_javascript_url_test.cc
@@ -0,0 +1,218 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_get_javascript_url_test.h"
+
+#include "base/basictypes.h"
+
+// url for "self".
+#define SELF_URL "javascript:window.location+\"\""
+// The identifier for the self url stream.
+#define SELF_URL_STREAM_ID 1
+
+// The identifier for the fetched url stream.
+#define FETCHED_URL_STREAM_ID 2
+
+// The maximum chunk size of stream data.
+#define STREAM_CHUNK 197
+
+const int kNPNEvaluateTimerID = 100;
+const int kNPNEvaluateTimerElapse = 50;
+
+
+namespace NPAPIClient {
+
+ExecuteGetJavascriptUrlTest::ExecuteGetJavascriptUrlTest(
+ NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ test_started_(false),
+#ifdef OS_WIN
+ window_(NULL),
+#endif
+ npn_evaluate_context_(false) {
+}
+
+NPError ExecuteGetJavascriptUrlTest::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ if (!test_started_) {
+ std::string url = SELF_URL;
+ HostFunctions()->geturlnotify(id(), url.c_str(), "_top",
+ reinterpret_cast<void*>(SELF_URL_STREAM_ID));
+ test_started_ = true;
+
+#ifdef OS_WIN
+ HWND window_handle = reinterpret_cast<HWND>(pNPWindow->window);
+ if (!::GetProp(window_handle, L"Plugin_Instance")) {
+ // TODO: this propery leaks.
+ ::SetProp(window_handle, L"Plugin_Instance", this);
+ // We attempt to retreive the NPObject for the plugin instance identified
+ // by the NPObjectLifetimeTestInstance2 class as it may not have been
+ // instantiated yet.
+ SetTimer(window_handle, kNPNEvaluateTimerID, kNPNEvaluateTimerElapse,
+ TimerProc);
+ }
+ window_ = window_handle;
+#endif
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+#ifdef OS_WIN
+void CALLBACK ExecuteGetJavascriptUrlTest::TimerProc(
+ HWND window, UINT message, UINT timer_id, unsigned long elapsed_time) {
+ ExecuteGetJavascriptUrlTest* this_instance =
+ reinterpret_cast<ExecuteGetJavascriptUrlTest*>
+ (::GetProp(window, L"Plugin_Instance"));
+
+ ::RemoveProp(window, L"Plugin_Instance");
+
+ NPObject *window_obj = NULL;
+ this_instance->HostFunctions()->getvalue(this_instance->id(),
+ NPNVWindowNPObject,
+ &window_obj);
+ if (!window_obj) {
+ this_instance->SetError("Failed to get NPObject for plugin instance2");
+ this_instance->SignalTestCompleted();
+ return;
+ }
+
+ std::string script = "javascript:window.location";
+ NPString script_string;
+ script_string.UTF8Characters = script.c_str();
+ script_string.UTF8Length = static_cast<unsigned int>(script.length());
+ NPVariant result_var;
+
+ this_instance->npn_evaluate_context_ = true;
+ NPError result = this_instance->HostFunctions()->evaluate(
+ this_instance->id(), window_obj, &script_string, &result_var);
+ this_instance->npn_evaluate_context_ = false;
+}
+#endif
+
+NPError ExecuteGetJavascriptUrlTest::NewStream(NPMIMEType type,
+ NPStream* stream,
+ NPBool seekable,
+ uint16* stype) {
+ if (stream == NULL) {
+ SetError("NewStream got null stream");
+ return NPERR_INVALID_PARAM;
+ }
+
+ if (npn_evaluate_context_) {
+ SetError("NewStream received in context of NPN_Evaluate");
+ return NPERR_NO_ERROR;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+ unsigned long stream_id = reinterpret_cast<unsigned long>(stream->notifyData);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+ return NPERR_NO_ERROR;
+}
+
+int32 ExecuteGetJavascriptUrlTest::WriteReady(NPStream *stream) {
+ if (npn_evaluate_context_) {
+ SetError("WriteReady received in context of NPN_Evaluate");
+ return NPERR_NO_ERROR;
+ }
+ return STREAM_CHUNK;
+}
+
+int32 ExecuteGetJavascriptUrlTest::Write(NPStream *stream, int32 offset,
+ int32 len, void *buffer) {
+ if (stream == NULL) {
+ SetError("Write got null stream");
+ return -1;
+ }
+ if (len < 0 || len > STREAM_CHUNK) {
+ SetError("Write got bogus stream chunk size");
+ return -1;
+ }
+
+ if (npn_evaluate_context_) {
+ SetError("Write received in context of NPN_Evaluate");
+ return len;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+ unsigned long stream_id = reinterpret_cast<unsigned long>(stream->notifyData);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ self_url_.append(static_cast<char*>(buffer), len);
+ break;
+ default:
+ SetError("Unexpected write callback");
+ break;
+ }
+ // Pretend that we took all the data.
+ return len;
+}
+
+
+NPError ExecuteGetJavascriptUrlTest::DestroyStream(NPStream *stream,
+ NPError reason) {
+ if (stream == NULL) {
+ SetError("NewStream got null stream");
+ return NPERR_INVALID_PARAM;
+ }
+
+#ifdef OS_WIN
+ KillTimer(window_, kNPNEvaluateTimerID);
+#endif
+
+ if (npn_evaluate_context_) {
+ SetError("DestroyStream received in context of NPN_Evaluate");
+ return NPERR_NO_ERROR;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+ unsigned long stream_id = reinterpret_cast<unsigned long>(stream->notifyData);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ // don't care
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+ return NPERR_NO_ERROR;
+}
+
+void ExecuteGetJavascriptUrlTest::URLNotify(const char* url, NPReason reason,
+ void* data) {
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(data),
+ cast_validity_check);
+
+ if (npn_evaluate_context_) {
+ SetError("URLNotify received in context of NPN_Evaluate");
+ return;
+ }
+
+ unsigned long stream_id = reinterpret_cast<unsigned long>(data);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ if (strcmp(url, SELF_URL) != 0)
+ SetError("URLNotify reported incorrect url for SELF_URL");
+ if (self_url_.empty())
+ SetError("Failed to obtain window location.");
+ SignalTestCompleted();
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_get_javascript_url_test.h b/webkit/glue/plugins/test/plugin_get_javascript_url_test.h
new file mode 100644
index 0000000..5c2540d
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_get_javascript_url_test.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_GET_JAVASCRIPT_URL_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_GET_JAVASCRIPT_URL_H
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class tests NPP_GetURLNotify for a javascript URL with _top
+// as the target frame.
+class ExecuteGetJavascriptUrlTest : public PluginTest {
+ public:
+ // Constructor.
+ ExecuteGetJavascriptUrlTest(NPP id, NPNetscapeFuncs *host_functions);
+ //
+ // NPAPI functions
+ //
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+ virtual NPError NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype);
+ virtual int32 WriteReady(NPStream *stream);
+ virtual int32 Write(NPStream *stream, int32 offset, int32 len,
+ void *buffer);
+ virtual NPError DestroyStream(NPStream *stream, NPError reason);
+ virtual void URLNotify(const char* url, NPReason reason, void* data);
+
+ private:
+#if defined(OS_WIN)
+ static void CALLBACK TimerProc(HWND window, UINT message, UINT timer_id,
+ unsigned long elapsed_time);
+#endif
+ bool test_started_;
+ // This flag is set to true in the context of the NPN_Evaluate call.
+ bool npn_evaluate_context_;
+ std::string self_url_;
+
+#if defined(OS_WIN)
+ HWND window_;
+#endif
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_GET_JAVASCRIPT_URL_H
diff --git a/webkit/glue/plugins/test/plugin_geturl_test.cc b/webkit/glue/plugins/test/plugin_geturl_test.cc
new file mode 100644
index 0000000..5363a66
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_geturl_test.cc
@@ -0,0 +1,414 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_geturl_test.h"
+
+#include <stdio.h>
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+
+// url for "self". The %22%22 is to make a statement for javascript to
+// evaluate and return.
+#define SELF_URL "javascript:window.location+\"\""
+
+// The identifier for the self url stream.
+#define SELF_URL_STREAM_ID 1
+
+// The identifier for the fetched url stream.
+#define FETCHED_URL_STREAM_ID 2
+
+// url for testing GetURL with a bogus URL.
+#define BOGUS_URL "bogoproto:///x:/asdf.xysdhffieasdf.asdhj/"
+
+// url for testing redirect notifications sent to plugins.
+#define REDIRECT_SRC_URL \
+ "http://mock.http/npapi/plugin_read_page_redirect_src.html"
+
+// The notification id for the redirect notification url.
+#define REDIRECT_SRC_URL_NOTIFICATION_ID 4
+
+// The identifier for the bogus url stream.
+#define BOGUS_URL_STREAM_ID 3
+
+// The maximum chunk size of stream data.
+#define STREAM_CHUNK 197
+
+namespace NPAPIClient {
+
+PluginGetURLTest::PluginGetURLTest(NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ tests_started_(false),
+ tests_in_progress_(0),
+ test_file_(NULL),
+ expect_404_response_(false),
+ npn_evaluate_context_(false),
+ handle_url_redirects_(false),
+ received_url_redirect_notification_(false) {
+}
+
+NPError PluginGetURLTest::New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved) {
+ const char* page_not_found_url = GetArgValue("page_not_found_url", argc,
+ argn, argv);
+ if (page_not_found_url) {
+ page_not_found_url_ = page_not_found_url;
+ expect_404_response_ = true;
+ }
+
+ const char* fail_write_url = GetArgValue("fail_write_url", argc,
+ argn, argv);
+ if (fail_write_url) {
+ fail_write_url_ = fail_write_url;
+ }
+
+ const char* referrer_target_url = GetArgValue("ref_target", argc,
+ argn, argv);
+ if (referrer_target_url) {
+ referrer_target_url_ = referrer_target_url;
+ }
+
+ if (!base::strcasecmp(GetArgValue("name", argc, argn, argv),
+ "geturlredirectnotify")) {
+ handle_url_redirects_ = true;
+ }
+ return PluginTest::New(mode, argc, argn, argv, saved);
+}
+
+NPError PluginGetURLTest::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ if (!tests_started_) {
+ tests_started_ = true;
+
+ tests_in_progress_++;
+
+ if (expect_404_response_) {
+ HostFunctions()->geturl(id(), page_not_found_url_.c_str(), NULL);
+ return NPERR_NO_ERROR;
+ } else if (!fail_write_url_.empty()) {
+ HostFunctions()->geturl(id(), fail_write_url_.c_str(), NULL);
+ return NPERR_NO_ERROR;
+ } else if (!referrer_target_url_.empty()) {
+ HostFunctions()->pushpopupsenabledstate(id(), true);
+ HostFunctions()->geturl(id(), referrer_target_url_.c_str(), "_blank");
+ HostFunctions()->poppopupsenabledstate(id());
+ return NPERR_NO_ERROR;
+ } else if (handle_url_redirects_) {
+ HostFunctions()->geturlnotify(
+ id(), REDIRECT_SRC_URL, NULL,
+ reinterpret_cast<void*>(REDIRECT_SRC_URL_NOTIFICATION_ID));
+ return NPERR_NO_ERROR;
+ }
+
+ std::string url = SELF_URL;
+ HostFunctions()->geturlnotify(id(), url.c_str(), NULL,
+ reinterpret_cast<void*>(SELF_URL_STREAM_ID));
+
+ tests_in_progress_++;
+ std::string bogus_url = BOGUS_URL;
+ HostFunctions()->geturlnotify(id(), bogus_url.c_str(), NULL,
+ reinterpret_cast<void*>(BOGUS_URL_STREAM_ID));
+ }
+ return NPERR_NO_ERROR;
+}
+
+NPError PluginGetURLTest::NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype) {
+ if (stream == NULL) {
+ SetError("NewStream got null stream");
+ return NPERR_INVALID_PARAM;
+ }
+
+ if (test_completed()) {
+ return PluginTest::NewStream(type, stream, seekable, stype);
+ }
+
+ if (!referrer_target_url_.empty()) {
+ return NPERR_NO_ERROR;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+
+ if (expect_404_response_) {
+ NPObject *window_obj = NULL;
+ HostFunctions()->getvalue(id(), NPNVWindowNPObject, &window_obj);
+ if (!window_obj) {
+ SetError("Failed to get NPObject for plugin instance2");
+ SignalTestCompleted();
+ return NPERR_NO_ERROR;
+ }
+
+ std::string script = "javascript:alert('Hi there from plugin');";
+ NPString script_string;
+ script_string.UTF8Characters = script.c_str();
+ script_string.UTF8Length = static_cast<unsigned int>(script.length());
+ NPVariant result_var;
+
+ npn_evaluate_context_ = true;
+ HostFunctions()->evaluate(id(), window_obj, &script_string, &result_var);
+ npn_evaluate_context_ = false;
+ return NPERR_NO_ERROR;
+ }
+
+ if (!fail_write_url_.empty()) {
+ return NPERR_NO_ERROR;
+ }
+
+
+ unsigned long stream_id = reinterpret_cast<unsigned long>(
+ stream->notifyData);
+
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ break;
+ case FETCHED_URL_STREAM_ID:
+ {
+ std::string filename = self_url_;
+ if (filename.find("file:///", 0) != 0) {
+ SetError("Test expects a file-url.");
+ break;
+ }
+
+ // TODO(evanm): use the net:: functions to convert file:// URLs to
+ // on-disk file paths. But it probably doesn't actually matter in
+ // this test.
+
+#if defined(OS_WIN)
+ filename = filename.substr(8); // remove "file:///"
+ // Assume an ASCII path on Windows.
+ FilePath path = FilePath(ASCIIToWide(filename));
+#else
+ filename = filename.substr(7); // remove "file://"
+ FilePath path = FilePath(filename);
+#endif
+
+ test_file_ = file_util::OpenFile(path, "r");
+ if (!test_file_) {
+ SetError("Could not open source file");
+ }
+ }
+ break;
+ case BOGUS_URL_STREAM_ID:
+ SetError("Unexpected NewStream for BOGUS_URL");
+ break;
+ case REDIRECT_SRC_URL_NOTIFICATION_ID:
+ SetError("Should not redirect to URL when plugin denied it.");
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+ return NPERR_NO_ERROR;
+}
+
+int32 PluginGetURLTest::WriteReady(NPStream *stream) {
+ if (test_completed()) {
+ return PluginTest::WriteReady(stream);
+ }
+
+ if (!referrer_target_url_.empty()) {
+ return STREAM_CHUNK;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+ unsigned long stream_id = reinterpret_cast<unsigned long>(
+ stream->notifyData);
+ if (stream_id == BOGUS_URL_STREAM_ID)
+ SetError("Received WriteReady for BOGUS_URL");
+
+ return STREAM_CHUNK;
+}
+
+int32 PluginGetURLTest::Write(NPStream *stream, int32 offset, int32 len,
+ void *buffer) {
+ if (test_completed()) {
+ return PluginTest::Write(stream, offset, len, buffer);
+ }
+
+ if (!fail_write_url_.empty()) {
+ SignalTestCompleted();
+ return -1;
+ }
+
+ if (!referrer_target_url_.empty()) {
+ return len;
+ }
+
+ if (stream == NULL) {
+ SetError("Write got null stream");
+ return -1;
+ }
+ if (len < 0 || len > STREAM_CHUNK) {
+ SetError("Write got bogus stream chunk size");
+ return -1;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+ unsigned long stream_id = reinterpret_cast<unsigned long>(
+ stream->notifyData);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ self_url_.append(static_cast<char*>(buffer), len);
+ break;
+ case FETCHED_URL_STREAM_ID:
+ {
+ char read_buffer[STREAM_CHUNK];
+ int32 bytes = fread(read_buffer, 1, len, test_file_);
+ // Technically, fread could return fewer than len
+ // bytes. But this is not likely.
+ if (bytes != len)
+ SetError("Did not read correct bytelength from source file");
+ if (memcmp(read_buffer, buffer, len))
+ SetError("Content mismatch between data and source!");
+ }
+ break;
+ case BOGUS_URL_STREAM_ID:
+ SetError("Unexpected write callback for BOGUS_URL");
+ break;
+ default:
+ SetError("Unexpected write callback");
+ break;
+ }
+ // Pretend that we took all the data.
+ return len;
+}
+
+
+NPError PluginGetURLTest::DestroyStream(NPStream *stream, NPError reason) {
+ if (test_completed()) {
+ return PluginTest::DestroyStream(stream, reason);
+ }
+
+ if (stream == NULL) {
+ SetError("NewStream got null stream");
+ return NPERR_INVALID_PARAM;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+
+ if (expect_404_response_) {
+ if (npn_evaluate_context_) {
+ SetError("Received destroyStream in the context of NPN_Evaluate.");
+ }
+
+ SignalTestCompleted();
+ return NPERR_NO_ERROR;
+ }
+
+ if (!referrer_target_url_.empty()) {
+ return NPERR_NO_ERROR;
+ }
+
+ unsigned long stream_id =
+ reinterpret_cast<unsigned long>(stream->notifyData);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ // don't care
+ break;
+ case FETCHED_URL_STREAM_ID:
+ {
+ char read_buffer[STREAM_CHUNK];
+ size_t bytes = fread(read_buffer, 1, sizeof(read_buffer), test_file_);
+ if (bytes != 0)
+ SetError("Data and source mismatch on length");
+ file_util::CloseFile(test_file_);
+ }
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+ return NPERR_NO_ERROR;
+}
+
+void PluginGetURLTest::StreamAsFile(NPStream* stream, const char* fname) {
+ if (stream == NULL) {
+ SetError("NewStream got null stream");
+ return;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
+ cast_validity_check);
+ unsigned long stream_id =
+ reinterpret_cast<unsigned long>(stream->notifyData);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ // don't care
+ break;
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+}
+
+void PluginGetURLTest::URLNotify(const char* url, NPReason reason, void* data) {
+ if (!tests_in_progress_) {
+ SetError("URLNotify received after tests completed");
+ return;
+ }
+
+ if (!url) {
+ SetError("URLNotify received NULL url");
+ return;
+ }
+
+ COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(data), cast_validity_check);
+ unsigned long stream_id = reinterpret_cast<unsigned long>(data);
+ switch (stream_id) {
+ case SELF_URL_STREAM_ID:
+ if (strcmp(url, SELF_URL) != 0)
+ SetError("URLNotify reported incorrect url for SELF_URL");
+
+ // We have our stream url. Go fetch it.
+ HostFunctions()->geturlnotify(id(), self_url_.c_str(), NULL,
+ reinterpret_cast<void*>(FETCHED_URL_STREAM_ID));
+ break;
+ case FETCHED_URL_STREAM_ID:
+ if (!url || strcmp(url, self_url_.c_str()) != 0)
+ SetError("URLNotify reported incorrect url for FETCHED_URL");
+ tests_in_progress_--;
+ break;
+ case BOGUS_URL_STREAM_ID:
+ if (reason != NPRES_NETWORK_ERR) {
+ std::string err = "BOGUS_URL received unexpected URLNotify status: ";
+ err.append(base::IntToString(reason));
+ SetError(err);
+ }
+ tests_in_progress_--;
+ break;
+ case REDIRECT_SRC_URL_NOTIFICATION_ID: {
+ if (!received_url_redirect_notification_) {
+ SetError("Failed to receive URLRedirect notification");
+ }
+ tests_in_progress_--;
+ break;
+ }
+ default:
+ SetError("Unexpected NewStream callback");
+ break;
+ }
+
+ if (tests_in_progress_ == 0)
+ SignalTestCompleted();
+}
+
+void PluginGetURLTest::URLRedirectNotify(const char* url,
+ int32_t status,
+ void* notify_data) {
+ if (!base::strcasecmp(url, "http://mock.http/npapi/plugin_read_page.html")) {
+ received_url_redirect_notification_ = true;
+ // Disallow redirect notification.
+ HostFunctions()->urlredirectresponse(id(), notify_data, false);
+ }
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_geturl_test.h b/webkit/glue/plugins/test/plugin_geturl_test.h
new file mode 100644
index 0000000..df8d741
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_geturl_test.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_PORT_PLUGINS_TEST_PLUGIN_GETURL_TEST_H__
+#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_GETURL_TEST_H__
+
+#include <stdio.h>
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// The PluginGetURLTest test functionality of the NPN_GetURL
+// and NPN_GetURLNotify methods.
+//
+// This test first discovers it's URL by sending a GetURL request
+// for 'javascript:top.location'. After receiving that, the
+// test will request the url itself (again via GetURL).
+class PluginGetURLTest : public PluginTest {
+ public:
+ // Constructor.
+ PluginGetURLTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ //
+ // NPAPI functions
+ //
+ virtual NPError New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved);
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+ virtual NPError NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype);
+ virtual int32 WriteReady(NPStream *stream);
+ virtual int32 Write(NPStream *stream, int32 offset, int32 len,
+ void *buffer);
+ virtual NPError DestroyStream(NPStream *stream, NPError reason);
+ virtual void StreamAsFile(NPStream* stream, const char* fname);
+ virtual void URLNotify(const char* url, NPReason reason, void* data);
+ virtual void URLRedirectNotify(const char* url, int32_t status,
+ void* notify_data);
+
+ private:
+ bool tests_started_;
+ int tests_in_progress_;
+ std::string self_url_;
+ FILE* test_file_;
+ bool expect_404_response_;
+ // This flag is set to true in the context of the NPN_Evaluate call.
+ bool npn_evaluate_context_;
+ // The following two flags handle URL redirect notifications received by
+ // plugins.
+ bool handle_url_redirects_;
+ bool received_url_redirect_notification_;
+ std::string page_not_found_url_;
+ std::string fail_write_url_;
+ std::string referrer_target_url_;
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_GETURL_TEST_H__
diff --git a/webkit/glue/plugins/test/plugin_javascript_open_popup.cc b/webkit/glue/plugins/test/plugin_javascript_open_popup.cc
new file mode 100644
index 0000000..0f93bf4
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_javascript_open_popup.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+#include "webkit/glue/plugins/test/plugin_javascript_open_popup.h"
+
+#if defined(USE_X11)
+#include "third_party/npapi/bindings/npapi_x11.h"
+#endif
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+namespace NPAPIClient {
+
+ExecuteJavascriptOpenPopupWithPluginTest::
+ ExecuteJavascriptOpenPopupWithPluginTest(NPP id,
+ NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ popup_window_test_started_(false) {
+}
+
+int16 ExecuteJavascriptOpenPopupWithPluginTest::SetWindow(
+ NPWindow* window) {
+ if (window->window == NULL)
+ return NPERR_NO_ERROR;
+
+ if (!popup_window_test_started_) {
+ popup_window_test_started_ = true;
+ HostFunctions()->geturl(
+ id(), "popup_window_with_target_plugin.html", "_blank");
+ }
+ return NPERR_NO_ERROR;
+}
+
+// ExecuteJavascriptPopupWindowTargetPluginTest member defines.
+ExecuteJavascriptPopupWindowTargetPluginTest::
+ ExecuteJavascriptPopupWindowTargetPluginTest(
+ NPP id, NPNetscapeFuncs* host_functions)
+ : PluginTest(id, host_functions),
+ test_completed_(false) {
+}
+
+int16 ExecuteJavascriptPopupWindowTargetPluginTest::SetWindow(
+ NPWindow* window) {
+ if (window->window == NULL)
+ return NPERR_NO_ERROR;
+
+ if (!test_completed_) {
+ if (CheckWindow(window)) {
+ SignalTestCompleted();
+ test_completed_ = true;
+ }
+ }
+ return PluginTest::SetWindow(window);
+}
+
+#if defined(OS_WIN)
+bool ExecuteJavascriptPopupWindowTargetPluginTest::CheckWindow(
+ NPWindow* window) {
+ HWND window_handle = reinterpret_cast<HWND>(window->window);
+
+ if (IsWindow(window_handle)) {
+ HWND parent_window = GetParent(window_handle);
+ if (!IsWindow(parent_window) || parent_window == GetDesktopWindow())
+ SetError("Windowed plugin instantiated with NULL parent");
+ return true;
+ }
+
+ return false;
+}
+
+#elif defined(USE_X11)
+// This code blindly follows the same sorts of verifications done on
+// the Windows side. Does it make sense on X? Maybe not really, but
+// it can't hurt to do extra validations.
+bool ExecuteJavascriptPopupWindowTargetPluginTest::CheckWindow(
+ NPWindow* window) {
+ Window xwindow = reinterpret_cast<Window>(window->window);
+ // Grab a pointer to the extra SetWindow data so we can grab the display out.
+ NPSetWindowCallbackStruct* extra =
+ static_cast<NPSetWindowCallbackStruct*>(window->ws_info);
+
+ if (xwindow) {
+ Window root, parent;
+ Status status = XQueryTree(extra->display, xwindow, &root, &parent,
+ NULL, NULL); // NULL children info.
+ DCHECK(status != 0);
+ if (!parent || parent == root)
+ SetError("Windowed plugin instantiated with NULL parent");
+ return true;
+ }
+
+ return false;
+}
+#elif defined(OS_MACOSX)
+bool ExecuteJavascriptPopupWindowTargetPluginTest::CheckWindow(
+ NPWindow* window) {
+ // TODO(port) scaffolding--replace with a real test once NPWindow is done.
+ return false;
+}
+#endif
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_javascript_open_popup.h b/webkit/glue/plugins/test/plugin_javascript_open_popup.h
new file mode 100644
index 0000000..552397a
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_javascript_open_popup.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_JAVASCRIPT_OPEN_POPUP_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_JAVASCRIPT_OPEN_POPUP_H
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class tests the case where a windowed plugin instance is
+// instantiated in a popup window. The plugin instance needs to
+// have a valid parent window.
+class ExecuteJavascriptOpenPopupWithPluginTest : public PluginTest {
+ public:
+ // Constructor.
+ ExecuteJavascriptOpenPopupWithPluginTest(
+ NPP id, NPNetscapeFuncs *host_functions);
+ // NPAPI SetWindow handler.
+ virtual NPError SetWindow(NPWindow* window);
+
+ private:
+ bool popup_window_test_started_;
+};
+
+// This class represents a windowed plugin instance instantiated within a
+// popup window. It verifies that the plugin instance has a valid parent.
+class ExecuteJavascriptPopupWindowTargetPluginTest : public PluginTest {
+ public:
+ ExecuteJavascriptPopupWindowTargetPluginTest(
+ NPP id, NPNetscapeFuncs *host_functions);
+ // NPAPI SetWindow handler.
+ virtual NPError SetWindow(NPWindow* window);
+
+ private:
+ // Do a platform-specific validation of the passed-in |window|.
+ // E.g. on Windows, verifies window->window is a reasonable HWND.
+ // Returns true if the test should be marked complete.
+ bool CheckWindow(NPWindow* window);
+
+ bool test_completed_;
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_JAVASCRIPT_OPEN_POPUP_H
diff --git a/webkit/glue/plugins/test/plugin_new_fails_test.cc b/webkit/glue/plugins/test/plugin_new_fails_test.cc
new file mode 100644
index 0000000..2feeec6
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_new_fails_test.cc
@@ -0,0 +1,18 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_new_fails_test.h"
+
+namespace NPAPIClient {
+
+NewFailsTest::NewFailsTest(NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions) {
+}
+
+NPError NewFailsTest::New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved) {
+ return NPERR_GENERIC_ERROR;
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_new_fails_test.h b/webkit/glue/plugins/test/plugin_new_fails_test.h
new file mode 100644
index 0000000..1acf9e5
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_new_fails_test.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_PLUGIN_NEW_FAILS_TEST_H__
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_PLUGIN_NEW_FAILS_TEST_H__
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+class NewFailsTest : public PluginTest {
+ public:
+ NewFailsTest(NPP id, NPNetscapeFuncs *host_functions);
+ virtual NPError New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved);
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_PLUGIN_NPP_NEW_FAILS_TEST_H__
diff --git a/webkit/glue/plugins/test/plugin_npobject_lifetime_test.cc b/webkit/glue/plugins/test/plugin_npobject_lifetime_test.cc
new file mode 100644
index 0000000..4564506
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_npobject_lifetime_test.cc
@@ -0,0 +1,174 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_npobject_lifetime_test.h"
+
+namespace NPAPIClient {
+
+const int kNPObjectLifetimeTimer = 100;
+const int kNPObjectLifetimeTimerElapse = 2000;
+
+NPObject* NPObjectLifetimeTestInstance2::plugin_instance_object_ = NULL;
+
+NPObjectDeletePluginInNPN_Evaluate*
+ NPObjectDeletePluginInNPN_Evaluate::g_npn_evaluate_test_instance_ = NULL;
+
+NPObjectLifetimeTest::NPObjectLifetimeTest(NPP id,
+ NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ other_plugin_instance_object_(NULL),
+ timer_id_(0) {
+}
+
+NPError NPObjectLifetimeTest::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ HWND window_handle = reinterpret_cast<HWND>(pNPWindow->window);
+ if (!::GetProp(window_handle, L"Plugin_Instance")) {
+ // TODO: this propery leaks.
+ ::SetProp(window_handle, L"Plugin_Instance", this);
+ // We attempt to retreive the NPObject for the plugin instance identified
+ // by the NPObjectLifetimeTestInstance2 class as it may not have been
+ // instantiated yet.
+ timer_id_ = SetTimer(window_handle, kNPObjectLifetimeTimer,
+ kNPObjectLifetimeTimerElapse, TimerProc);
+ }
+ return NPERR_NO_ERROR;
+}
+
+void CALLBACK NPObjectLifetimeTest::TimerProc(
+ HWND window, UINT message, UINT timer_id,
+ unsigned long elapsed_milli_seconds) {
+
+ NPObjectLifetimeTest* this_instance =
+ reinterpret_cast<NPObjectLifetimeTest*>
+ (::GetProp(window, L"Plugin_Instance"));
+ KillTimer(window, this_instance->timer_id_);
+ ::RemoveProp(window, L"Plugin_Instance");
+
+ this_instance->timer_id_ = 0;
+
+ this_instance->other_plugin_instance_object_ =
+ NPObjectLifetimeTestInstance2::plugin_instance_object_;
+ this_instance->HostFunctions()->retainobject(
+ this_instance->other_plugin_instance_object_);
+
+ NPString script;
+ script.UTF8Characters = "javascript:DeleteSecondPluginInstance()";
+ script.UTF8Length = static_cast<uint32_t>(strlen(script.UTF8Characters));
+
+ this_instance->HostFunctions()->geturlnotify(
+ this_instance->id(), "javascript:DeleteSecondPluginInstance()", NULL,
+ reinterpret_cast<void*>(1));
+}
+
+void NPObjectLifetimeTest::URLNotify(const char* url, NPReason reason,
+ void* data) {
+ // Create a "location" identifier.
+ NPIdentifier identifier = HostFunctions()->getstringidentifier("location");
+ // Declare a local variant value.
+ NPVariant variantValue;
+ // Get the location property from the window object (which is another object).
+ bool b1 = HostFunctions()->getproperty(id(), other_plugin_instance_object_,
+ identifier, &variantValue );
+ HostFunctions()->releaseobject(other_plugin_instance_object_);
+ other_plugin_instance_object_ = NULL;
+ // If this test failed, then we'd have crashed by now.
+ SignalTestCompleted();
+}
+
+NPObjectLifetimeTestInstance2::NPObjectLifetimeTestInstance2(
+ NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions) {
+}
+
+NPObjectLifetimeTestInstance2::~NPObjectLifetimeTestInstance2() {
+ if (plugin_instance_object_) {
+ HostFunctions()->releaseobject(plugin_instance_object_);
+ plugin_instance_object_ = NULL;
+ }
+}
+
+NPError NPObjectLifetimeTestInstance2::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ if (!plugin_instance_object_) {
+ if (!HostFunctions()->getvalue(id(), NPNVWindowNPObject,
+ &plugin_instance_object_)) {
+ SetError("Failed to get NPObject for plugin instance2");
+ SignalTestCompleted();
+ return NPERR_GENERIC_ERROR;
+ }
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+
+NPObjectDeletePluginInNPN_Evaluate::NPObjectDeletePluginInNPN_Evaluate(
+ NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ plugin_instance_object_(NULL),
+ timer_id_(0) {
+ g_npn_evaluate_test_instance_ = this;
+}
+
+NPObjectDeletePluginInNPN_Evaluate::~NPObjectDeletePluginInNPN_Evaluate() {
+ if (plugin_instance_object_) {
+ HostFunctions()->releaseobject(plugin_instance_object_);
+ plugin_instance_object_ = NULL;
+ }
+}
+
+NPError NPObjectDeletePluginInNPN_Evaluate::SetWindow(NPWindow* np_window) {
+ if (np_window->window == NULL)
+ return NPERR_NO_ERROR;
+
+ HWND window_handle = reinterpret_cast<HWND>(np_window->window);
+ // We setup a timerproc to invoke NPN_Evaluate to destroy this plugin
+ // instance. This is to ensure that we don't destroy the plugin instance
+ // while it is being used in webkit as this leads to crashes and is a
+ // more accurate representation of the renderer crash as described in
+ // http://b/issue?id=1134683.
+ if (!timer_id_) {
+ timer_id_ = SetTimer(window_handle, kNPObjectLifetimeTimer,
+ kNPObjectLifetimeTimerElapse, TimerProc);
+ }
+ return NPERR_NO_ERROR;
+}
+
+void CALLBACK NPObjectDeletePluginInNPN_Evaluate::TimerProc(
+ HWND window, UINT message, UINT timer_id,
+ unsigned long elapsed_milli_seconds) {
+
+ KillTimer(window, g_npn_evaluate_test_instance_->timer_id_);
+ g_npn_evaluate_test_instance_->timer_id_ = 0;
+ NPObject *window_obj = NULL;
+ g_npn_evaluate_test_instance_->HostFunctions()->getvalue(
+ g_npn_evaluate_test_instance_->id(), NPNVWindowNPObject,
+ &window_obj);
+
+ if (!window_obj) {
+ g_npn_evaluate_test_instance_->SetError(
+ "Failed to get NPObject for plugin instance2");
+ g_npn_evaluate_test_instance_->SignalTestCompleted();
+ return;
+ }
+
+ std::string script = "javascript:DeletePluginWithinScript()";
+ NPString script_string;
+ script_string.UTF8Characters = script.c_str();
+ script_string.UTF8Length =
+ static_cast<unsigned int>(script.length());
+
+ NPVariant result_var;
+ bool result = g_npn_evaluate_test_instance_->HostFunctions()->evaluate(
+ g_npn_evaluate_test_instance_->id(), window_obj,
+ &script_string, &result_var);
+ // If this test failed we would have crashed by now.
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_npobject_lifetime_test.h b/webkit/glue/plugins/test/plugin_npobject_lifetime_test.h
new file mode 100644
index 0000000..60d0314
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_npobject_lifetime_test.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_LIFETIME_TEST_H__
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_LIFETIME_TEST_H__
+
+#include "build/build_config.h"
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// The NPObjectLifeTime class tests the case where a plugin has an NPObject
+// which points to a different plugin instance on a different frame in the
+// page and whether refcounts on this npobject are valid when the source frame
+// is destroyed.
+class NPObjectLifetimeTest : public PluginTest {
+ public:
+ // Constructor.
+ NPObjectLifetimeTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ // NPAPI SetWindow handler.
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+
+ virtual void URLNotify(const char* url, NPReason reason, void* data);
+
+ protected:
+ NPObject* other_plugin_instance_object_;
+
+#if defined(OS_WIN)
+ static void CALLBACK TimerProc(HWND window, UINT message, UINT timer_id,
+ unsigned long elapsed_milli_seconds);
+ UINT_PTR timer_id_;
+#endif
+ DISALLOW_IMPLICIT_CONSTRUCTORS(NPObjectLifetimeTest);
+};
+
+// The NPObjectLifetimeTestInstance2 class represents the plugin instance
+// which is deleted by the NPObjectLifeTime class via a javascript function.
+class NPObjectLifetimeTestInstance2 : public PluginTest {
+ public:
+ // Constructor.
+ NPObjectLifetimeTestInstance2(NPP id, NPNetscapeFuncs *host_functions);
+ ~NPObjectLifetimeTestInstance2();
+
+ // NPAPI SetWindow handler.
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+ protected:
+ static NPObject* plugin_instance_object_;
+ friend class NPObjectLifetimeTest;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(NPObjectLifetimeTestInstance2);
+};
+
+// The NPObjectLifeTime class tests the case where a plugin instance is
+// destroyed in NPN_Evaluate
+class NPObjectDeletePluginInNPN_Evaluate : public PluginTest {
+ public:
+ // Constructor.
+ NPObjectDeletePluginInNPN_Evaluate(NPP id, NPNetscapeFuncs *host_functions);
+ ~NPObjectDeletePluginInNPN_Evaluate();
+
+ // NPAPI SetWindow handler.
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+
+ protected:
+ NPObject* plugin_instance_object_;
+#if defined(OS_WIN)
+ static void CALLBACK TimerProc(HWND window, UINT message, UINT timer_id,
+ unsigned long elapsed_milli_seconds);
+ UINT_PTR timer_id_;
+#endif
+
+ private:
+ static NPObjectDeletePluginInNPN_Evaluate* g_npn_evaluate_test_instance_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(NPObjectDeletePluginInNPN_Evaluate);
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_LIFETIME_TEST_H__
diff --git a/webkit/glue/plugins/test/plugin_npobject_proxy_test.cc b/webkit/glue/plugins/test/plugin_npobject_proxy_test.cc
new file mode 100644
index 0000000..5b3a2ca
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_npobject_proxy_test.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+
+#if defined(OS_WIN)
+#define STRSAFE_NO_DEPRECATE
+#include <strsafe.h>
+#endif
+#include "webkit/glue/plugins/test/plugin_npobject_proxy_test.h"
+
+namespace NPAPIClient {
+
+NPObjectProxyTest::NPObjectProxyTest(NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions) {
+}
+
+NPError NPObjectProxyTest::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ NPIdentifier document_id = HostFunctions()->getstringidentifier("document");
+ NPIdentifier create_text_node_id = HostFunctions()->getstringidentifier("createTextNode");
+ NPIdentifier append_child_id = HostFunctions()->getstringidentifier("appendChild");
+
+ NPVariant docv;
+ NPObject *window_obj = NULL;
+ HostFunctions()->getvalue(id(), NPNVWindowNPObject, &window_obj);
+
+ HostFunctions()->getproperty(id(), window_obj, document_id, &docv);
+ NPObject *doc = NPVARIANT_TO_OBJECT(docv);
+
+ NPVariant strv;
+ MSVC_SUPPRESS_WARNING(4267);
+ STRINGZ_TO_NPVARIANT("div", strv);
+
+ NPVariant textv;
+ HostFunctions()->invoke(id(), doc, create_text_node_id, &strv, 1, &textv);
+
+ NPVariant v;
+ HostFunctions()->invoke(id(), doc, append_child_id, &textv, 1, &v);
+
+ // If this test failed, then we'd have crashed by now.
+ SignalTestCompleted();
+
+ return NPERR_NO_ERROR;
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_npobject_proxy_test.h b/webkit/glue/plugins/test/plugin_npobject_proxy_test.h
new file mode 100644
index 0000000..3d14ddb
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_npobject_proxy_test.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_PROXY_TEST_H__
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_PROXY_TEST_H__
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// The NPObjectProxyTest tests that when we proxy an NPObject that is itself
+// a proxy, we don't create a new proxy but instead just use the original
+// pointer.
+
+class NPObjectProxyTest : public PluginTest {
+ public:
+ // Constructor.
+ NPObjectProxyTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ // NPAPI SetWindow handler.
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_NPOBJECT_PROXY_TEST_H__
diff --git a/webkit/glue/plugins/test/plugin_private_test.cc b/webkit/glue/plugins/test/plugin_private_test.cc
new file mode 100644
index 0000000..cdab7ce
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_private_test.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_private_test.h"
+
+#include "base/basictypes.h"
+#include "base/string_util.h"
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+namespace NPAPIClient {
+
+PrivateTest::PrivateTest(NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions) {
+}
+
+NPError PrivateTest::New(uint16 mode, int16 argc,
+ const char* argn[], const char* argv[],
+ NPSavedData* saved) {
+ PluginTest::New(mode, argc, argn, argv, saved);
+
+ NPBool private_mode = 0;
+ NPNetscapeFuncs* browser = NPAPIClient::PluginClient::HostFunctions();
+ NPError result = browser->getvalue(
+ id(), NPNVprivateModeBool, &private_mode);
+ if (result != NPERR_NO_ERROR) {
+ SetError("Failed to read NPNVprivateModeBool value.");
+ } else {
+ NPIdentifier location = HostFunctions()->getstringidentifier("location");
+ NPIdentifier href = HostFunctions()->getstringidentifier("href");
+
+ NPObject *window_obj = NULL;
+ HostFunctions()->getvalue(id(), NPNVWindowNPObject, &window_obj);
+
+ NPVariant location_var;
+ HostFunctions()->getproperty(id(), window_obj, location, &location_var);
+
+ NPVariant href_var;
+ HostFunctions()->getproperty(id(), NPVARIANT_TO_OBJECT(location_var), href,
+ &href_var);
+ std::string href_str(href_var.value.stringValue.UTF8Characters,
+ href_var.value.stringValue.UTF8Length);
+ bool private_expected = href_str.find("?private") != href_str.npos;
+ if (private_mode != static_cast<NPBool>(private_expected))
+ SetError("NPNVprivateModeBool returned incorrect value.");
+
+ HostFunctions()->releasevariantvalue(&href_var);
+ HostFunctions()->releasevariantvalue(&location_var);
+ HostFunctions()->releaseobject(window_obj);
+ }
+
+ SignalTestCompleted();
+
+ return NPERR_NO_ERROR;
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_private_test.h b/webkit/glue/plugins/test/plugin_private_test.h
new file mode 100644
index 0000000..db6b5d1
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_private_test.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_PORT_PLUGINS_TEST_PLUGIN_PRIVATE_TEST_H_
+#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_PRIVATE_TEST_H_
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// The PluginPrivateTest tests that a plugin can query if the browser is in
+// private browsing mode.
+class PrivateTest : public PluginTest {
+ public:
+ PrivateTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ // Initialize this PluginTest based on the arguments from NPP_New.
+ virtual NPError New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved);
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_PRIVATE_TEST_H_
diff --git a/webkit/glue/plugins/test/plugin_schedule_timer_test.cc b/webkit/glue/plugins/test/plugin_schedule_timer_test.cc
new file mode 100644
index 0000000..fbfce34
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_schedule_timer_test.cc
@@ -0,0 +1,116 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_schedule_timer_test.h"
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+using base::Time;
+
+namespace NPAPIClient {
+
+// The times below are accurate but they are not tested against because it
+// might make the test flakey.
+ScheduleTimerTest::Event
+ ScheduleTimerTest::schedule_[ScheduleTimerTest::kNumEvents] = {
+ { 0, -1, 0, 100, false, -1 }, // schedule 0 100ms no-repeat
+ { 100, 0, 0, 200, false, -1 }, // schedule 0 200ms no-repeat
+ { 300, 0, 0, 100, true, -1 }, // schedule 0 100ms repeat
+ { 400, 0, 1, 50, true, -1 }, // schedule 1 50ms repeat
+ { 450, 1, -1, 0, true, -1 }, // receive 1 repeating
+ { 500, 0, -1, 0, true, -1 }, // receive 0 repeating
+ { 500, 1, -1, 0, true, -1 }, // receive 1 repeating
+ { 550, 1, -1, 0, true, -1 }, // receive 1 repeating
+ { 600, 0, -1, 0, true, 0 }, // receive 0 repeating and unschedule
+ { 600, 1, 2, 400, true, 1 }, // receive 1 repeating and unschedule
+ { 1000, 2, -1, 0, true, 2 }, // receive final and unschedule
+};
+
+ScheduleTimerTest::ScheduleTimerTest(
+ NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ num_received_events_(0) {
+ for (int i = 0; i < kNumTimers; ++i) {
+ timer_ids_[i] = 0;
+ }
+ for (int i = 0; i < kNumEvents; ++i) {
+ received_events_[i] = false;
+ }
+}
+
+NPError ScheduleTimerTest::New(
+ uint16 mode, int16 argc, const char* argn[], const char* argv[],
+ NPSavedData* saved) {
+ NPError error = PluginTest::New(mode, argc, argn, argv, saved);
+ if (error != NPERR_NO_ERROR)
+ return error;
+
+ start_time_ = Time::Now();
+ HandleEvent(0);
+
+ return NPERR_NO_ERROR;
+}
+
+void ScheduleTimerTest::OnTimer(uint32 timer_id) {
+ Time current_time = Time::Now();
+ int relative_time = static_cast<int>(
+ (current_time - start_time_).InMilliseconds());
+
+ // See if there is a matching unreceived event.
+ int event_index = FindUnreceivedEvent(relative_time, timer_id);
+ if (event_index < 0) {
+ SetError("Received unexpected timer event");
+ SignalTestCompleted();
+ return;
+ }
+
+ HandleEvent(event_index);
+
+ // Finish test if all events have happened.
+ if (num_received_events_ == kNumEvents)
+ SignalTestCompleted();
+}
+
+int ScheduleTimerTest::FindUnreceivedEvent(int time, uint32 timer_id) {
+ for (int i = 0; i < kNumEvents; ++i) {
+ const Event& event = schedule_[i];
+ if (!received_events_[i] &&
+ timer_ids_[event.received_index] == timer_id) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+namespace {
+void OnTimerHelper(NPP id, uint32 timer_id) {
+ ScheduleTimerTest* plugin_object =
+ static_cast<ScheduleTimerTest*>(id->pdata);
+ if (plugin_object) {
+ plugin_object->OnTimer(timer_id);
+ }
+}
+}
+
+void ScheduleTimerTest::HandleEvent(int event_index) {
+ const Event& event = schedule_[event_index];
+
+ // Mark event as received.
+ DCHECK(!received_events_[event_index]);
+ received_events_[event_index] = true;
+ ++num_received_events_;
+
+ // Unschedule timer if present.
+ if (event.unscheduled_index >= 0) {
+ HostFunctions()->unscheduletimer(
+ id(), timer_ids_[event.unscheduled_index]);
+ }
+
+ // Schedule timer if present.
+ if (event.scheduled_index >= 0) {
+ timer_ids_[event.scheduled_index] = HostFunctions()->scheduletimer(
+ id(), event.scheduled_interval, event.schedule_repeated, OnTimerHelper);
+ }
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_schedule_timer_test.h b/webkit/glue/plugins/test/plugin_schedule_timer_test.h
new file mode 100644
index 0000000..e3e6505a
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_schedule_timer_test.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_SCHEDULE_TIMER_TEST_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_SCHEDULE_TIMER_TEST_H
+
+#include "base/at_exit.h"
+#include "base/time.h"
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class tests scheduling and unscheduling of timers using
+// NPN_ScheduleTimer and NPN_UnscheduleTimer.
+class ScheduleTimerTest : public PluginTest {
+ public:
+ ScheduleTimerTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ virtual NPError New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved);
+
+ void OnTimer(uint32 timer_id);
+
+ private:
+ // base::Time needs one of these.
+ base::AtExitManager at_exit_manager_;
+
+ // Table mapping timer index (as used in event schedule) to timer id.
+ static const int kNumTimers = 3;
+ uint32 timer_ids_[kNumTimers];
+
+ // Schedule of events for test.
+ static const int kNumEvents = 11;
+ struct Event {
+ int time;
+
+ // The index of the timer that triggered the event or -1 for the first
+ // event.
+ int received_index;
+
+ // The index of the timer to schedule on this event or -1.
+ int scheduled_index;
+
+ // Info about the timer to be scheduled (if any).
+ uint32 scheduled_interval;
+ bool schedule_repeated;
+
+ // The index of the timer to unschedule on this event or -1.
+ int unscheduled_index;
+ };
+ static Event schedule_[kNumEvents];
+ int num_received_events_;
+
+ // Set of events that have been received (by index).
+ bool received_events_[kNumEvents];
+
+ // Time of initial event.
+ base::Time start_time_;
+
+ // Returns index of matching unreceived event or -1 if not found.
+ int FindUnreceivedEvent(int time, uint32 timer_id);
+ void HandleEvent(int event_index);
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_SCHEDULE_TIMER_TEST_H
diff --git a/webkit/glue/plugins/test/plugin_setup_test.cc b/webkit/glue/plugins/test/plugin_setup_test.cc
new file mode 100644
index 0000000..e4c4903
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_setup_test.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/string_util.h"
+
+#include "webkit/glue/plugins/test/plugin_setup_test.h"
+
+namespace NPAPIClient {
+
+PluginSetupTest::PluginSetupTest(NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions) {
+}
+
+NPError PluginSetupTest::SetWindow(NPWindow* pNPWindow) {
+ this->SignalTestCompleted();
+
+ return NPERR_NO_ERROR;
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_setup_test.h b/webkit/glue/plugins/test/plugin_setup_test.h
new file mode 100644
index 0000000..b01bc42
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_setup_test.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_SETUP_TEST_H__
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_SETUP_TEST_H__
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// A very simple test that just sets up a new plug-in.
+class PluginSetupTest : public PluginTest {
+ public:
+ // Constructor.
+ PluginSetupTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ // NPAPI SetWindow handler.
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_SETUP_TEST_H__
diff --git a/webkit/glue/plugins/test/plugin_test.cc b/webkit/glue/plugins/test/plugin_test.cc
new file mode 100644
index 0000000..6717e4b
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_test.cc
@@ -0,0 +1,155 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+#include "base/string_util.h"
+#include "webkit/glue/plugins/test/npapi_constants.h"
+
+namespace NPAPIClient {
+
+PluginTest::PluginTest(NPP id, NPNetscapeFuncs *host_functions) {
+ id_ = id;
+ id_->pdata = this;
+ host_functions_ = host_functions;
+ test_completed_ = false;
+}
+
+NPError PluginTest::New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved) {
+ test_name_ = this->GetArgValue("name", argc, argn, argv);
+ test_id_ = this->GetArgValue("id", argc, argn, argv);
+ return NPERR_NO_ERROR;
+}
+
+NPError PluginTest::Destroy() {
+ return NPERR_NO_ERROR;
+}
+
+NPError PluginTest::SetWindow(NPWindow* pNPWindow) {
+ return NPERR_NO_ERROR;
+}
+
+// It's a shame I have to implement URLEncode. But, using webkit's
+// or using chrome's means a ball of string of dlls and dependencies that
+// is very very long. After spending far too much time on it,
+// I'll just encode it myself. Too bad Microsoft doesn't implement
+// this in a reusable way either. Both webkit and chrome will
+// end up using libicu, which is a string of dependencies we don't
+// want.
+
+inline unsigned char toHex(const unsigned char x) {
+ return x > 9 ? (x + 'A' - 10) : (x + '0');
+}
+
+std::string URLEncode(const std::string &sIn) {
+ std::string sOut;
+
+ const size_t length = sIn.length();
+ for (size_t idx = 0; idx < length;) {
+ const char ch = sIn.at(idx);
+ if (isalnum(ch)) {
+ sOut.append(1, ch);
+ } else if (isspace(ch) && ((ch != '\n') && (ch != '\r'))) {
+ sOut.append(1, '+');
+ } else {
+ sOut.append(1, '%');
+ sOut.append(1, toHex(ch>>4));
+ sOut.append(1, toHex(ch%16));
+ }
+ idx++;
+ }
+ return sOut;
+}
+
+void PluginTest::SignalTestCompleted() {
+ NPObject *window_obj = NULL;
+ host_functions_->getvalue(id_, NPNVWindowNPObject, &window_obj);
+ if (!window_obj)
+ return;
+
+ test_completed_ = true;
+ // To signal test completion, we expect a couple of
+ // javascript functions to be defined in the webpage
+ // which hosts this plugin:
+ // onSuccess(test_name, test_id)
+ // onFailure(test_name, test_id, error_message)
+ std::string script("javascript:");
+ if (Succeeded()) {
+ script.append("onSuccess(\"");
+ script.append(test_name_);
+ script.append("\",\"");
+ script.append(test_id_);
+ script.append("\");");
+ } else {
+ script.append("onFailure(\"");
+ script.append(test_name_);
+ script.append("\",\"");
+ script.append(test_id_);
+ script.append("\",\"");
+ script.append(test_status_);
+ script.append("\");");
+ }
+
+ NPString script_string;
+ script_string.UTF8Characters = script.c_str();
+ script_string.UTF8Length = static_cast<unsigned int>(script.length());
+
+ NPVariant result_var;
+ host_functions_->evaluate(id_, window_obj, &script_string, &result_var);
+}
+
+const char *PluginTest::GetArgValue(const char *name, const int16 argc,
+ const char *argn[], const char *argv[]) {
+ for (int idx = 0; idx < argc; idx++)
+ if (base::strcasecmp(argn[idx], name) == 0)
+ return argv[idx];
+ return NULL;
+}
+
+void PluginTest::SetError(const std::string &msg) {
+ test_status_.append(msg);
+}
+
+NPError PluginTest::NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype) {
+ // There is no default action here.
+ return NPERR_NO_ERROR;
+}
+
+int32 PluginTest::WriteReady(NPStream *stream) {
+ // Take data in small chunks
+ return 4096;
+}
+
+int32 PluginTest::Write(NPStream *stream, int32 offset, int32 len,
+ void *buffer) {
+ // Pretend that we took all the data.
+ return len;
+}
+
+NPError PluginTest::DestroyStream(NPStream *stream, NPError reason) {
+ // There is no default action.
+ return NPERR_NO_ERROR;
+}
+
+void PluginTest::StreamAsFile(NPStream* stream, const char* fname) {
+ // There is no default action.
+}
+
+void PluginTest::URLNotify(const char* url, NPReason reason, void* data) {
+ // There is no default action
+}
+
+int16 PluginTest::HandleEvent(void* event) {
+ // There is no default action
+ return 0;
+}
+
+void PluginTest::URLRedirectNotify(const char* url, int32_t status,
+ void* notify_data) {
+ // There is no default action
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_test.h b/webkit/glue/plugins/test/plugin_test.h
new file mode 100644
index 0000000..f3f8937
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_test.h
@@ -0,0 +1,134 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_PORT_PLUGINS_TEST_PLUGIN_TEST_H_
+#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_TEST_H_
+
+#include <string>
+
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "third_party/npapi/bindings/npapi.h"
+#include "third_party/npapi/bindings/nphostapi.h"
+
+namespace NPAPIClient {
+
+// A PluginTest represents an instance of the plugin, which in
+// our case is a test case.
+class PluginTest {
+ public:
+ // Constructor.
+ PluginTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ // Destructor
+ virtual ~PluginTest() {}
+
+ // Returns true if the test runs in windowless plugin mode.
+ virtual bool IsWindowless() const { return false; }
+
+ //
+ // NPAPI Functions
+ //
+ virtual NPError New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved);
+ virtual NPError Destroy();
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+ virtual NPError NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype);
+ virtual int32 WriteReady(NPStream *stream);
+ virtual int32 Write(NPStream *stream, int32 offset, int32 len,
+ void *buffer);
+ virtual NPError DestroyStream(NPStream *stream, NPError reason);
+ virtual void StreamAsFile(NPStream* stream, const char* fname);
+ virtual void URLNotify(const char* url, NPReason reason, void* data);
+ virtual int16 HandleEvent(void* event);
+ virtual void URLRedirectNotify(const char* url, int32_t status,
+ void* notify_data);
+ // Returns true if the test has not had any errors.
+ bool Succeeded() { return test_status_.length() == 0; }
+
+ // Sets an error for the test case. Appends the msg to the
+ // error that will be returned from the test.
+ void SetError(const std::string &msg);
+
+ // Expect two string values are equal, and if not, logs an
+ // appropriate error about it.
+ void ExpectStringLowerCaseEqual(const std::string &val1, const std::string &val2) {
+ if (!LowerCaseEqualsASCII(val1, val2.c_str())) {
+ std::string err;
+ err = "Expected Equal for '";
+ err.append(val1);
+ err.append("' and '");
+ err.append(val2);
+ err.append("'");
+ SetError(err);
+ }
+ };
+
+ // Expect two values to not be equal, and if they are
+ // logs an appropriate error about it.
+ void ExpectAsciiStringNotEqual(const char *val1, const char *val2) {
+ if (val1 == val2) {
+ std::string err;
+ err = "Expected Not Equal for '";
+ err.append(val1);
+ err.append("' and '");
+ err.append(val2);
+ err.append("'");
+ SetError(err);
+ }
+ }
+ // Expect two integer values are equal, and if not, logs an
+ // appropriate error about it.
+ void ExpectIntegerEqual(int val1, int val2) {
+ if (val1 != val2) {
+ std::string err;
+ err = "Expected Equal for '";
+ err.append(base::IntToString(val1));
+ err.append("' and '");
+ err.append(base::IntToString(val2));
+ err.append("'");
+ SetError(err);
+ }
+ }
+
+
+ protected:
+ // Signals to the Test that invoked us that the test is
+ // completed. This is done by forcing the plugin to
+ // set a cookie in the browser window, which the test program
+ // is waiting for. Note - because this is done by
+ // using javascript, the browser must have the frame
+ // setup before the plugin calls this function. So plugin
+ // tests MUST NOT call this function prior to having
+ // received the SetWindow() callback from the browser.
+ void SignalTestCompleted();
+
+ // Helper function to lookup names in the input array.
+ // If the name is found, returns the value, otherwise
+ // returns NULL.
+ const char *GetArgValue(const char *name, const int16 argc,
+ const char *argn[], const char *argv[]);
+
+ // Access to the list of functions provided
+ // by the NPAPI host.
+ NPNetscapeFuncs *HostFunctions() { return host_functions_; }
+
+ // The NPP Identifier for this plugin instance.
+ NPP id() { return id_; }
+ std::string test_id() const { return test_id_; }
+ std::string test_name() const { return test_name_; }
+ bool test_completed() const { return test_completed_; }
+ private:
+ NPP id_;
+ NPNetscapeFuncs * host_functions_;
+ std::string test_name_;
+ std::string test_id_;
+ std::string test_status_;
+ bool test_completed_;
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_TEST_H_
diff --git a/webkit/glue/plugins/test/plugin_test_factory.cc b/webkit/glue/plugins/test/plugin_test_factory.cc
new file mode 100644
index 0000000..b4ae4f1
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_test_factory.cc
@@ -0,0 +1,104 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_test_factory.h"
+
+#include "webkit/glue/plugins/test/plugin_arguments_test.h"
+#include "webkit/glue/plugins/test/plugin_delete_plugin_in_stream_test.h"
+#include "webkit/glue/plugins/test/plugin_get_javascript_url_test.h"
+#include "webkit/glue/plugins/test/plugin_get_javascript_url2_test.h"
+#include "webkit/glue/plugins/test/plugin_geturl_test.h"
+#include "webkit/glue/plugins/test/plugin_javascript_open_popup.h"
+#include "webkit/glue/plugins/test/plugin_new_fails_test.h"
+#include "webkit/glue/plugins/test/plugin_npobject_lifetime_test.h"
+#include "webkit/glue/plugins/test/plugin_npobject_proxy_test.h"
+#include "webkit/glue/plugins/test/plugin_private_test.h"
+#include "webkit/glue/plugins/test/plugin_schedule_timer_test.h"
+#include "webkit/glue/plugins/test/plugin_setup_test.h"
+#include "webkit/glue/plugins/test/plugin_thread_async_call_test.h"
+#include "webkit/glue/plugins/test/plugin_window_size_test.h"
+#if defined(OS_WIN)
+#include "webkit/glue/plugins/test/plugin_windowed_test.h"
+#endif
+#include "webkit/glue/plugins/test/plugin_windowless_test.h"
+
+namespace NPAPIClient {
+
+PluginTest* CreatePluginTest(const std::string& test_name,
+ NPP instance,
+ NPNetscapeFuncs* host_functions) {
+ PluginTest* new_test = NULL;
+
+ if (test_name == "arguments") {
+ new_test = new PluginArgumentsTest(instance, host_functions);
+ } else if (test_name == "geturl" || test_name == "geturl_404_response" ||
+ test_name == "geturl_fail_write" ||
+ test_name == "plugin_referrer_test" ||
+ test_name == "geturlredirectnotify") {
+ new_test = new PluginGetURLTest(instance, host_functions);
+ } else if (test_name == "npobject_proxy") {
+ new_test = new NPObjectProxyTest(instance, host_functions);
+#if defined(OS_WIN) || defined(OS_MACOSX)
+ // TODO(port): plugin_windowless_test.*.
+ } else if (test_name == "execute_script_delete_in_paint" ||
+ test_name == "execute_script_delete_in_mouse_move" ||
+ test_name == "delete_frame_test" ||
+ test_name == "multiple_instances_sync_calls" ||
+ test_name == "no_hang_if_init_crashes" ||
+ test_name == "convert_point") {
+ new_test = new WindowlessPluginTest(instance, host_functions);
+#endif
+ } else if (test_name == "getjavascripturl") {
+ new_test = new ExecuteGetJavascriptUrlTest(instance, host_functions);
+ } else if (test_name == "getjavascripturl2") {
+ new_test = new ExecuteGetJavascriptUrl2Test(instance, host_functions);
+#if defined(OS_WIN)
+ // TODO(port): plugin_window_size_test.*.
+ } else if (test_name == "checkwindowrect") {
+ new_test = new PluginWindowSizeTest(instance, host_functions);
+#endif
+ } else if (test_name == "self_delete_plugin_stream") {
+ new_test = new DeletePluginInStreamTest(instance, host_functions);
+#if defined(OS_WIN)
+ // TODO(port): plugin_npobject_lifetime_test.*.
+ } else if (test_name == "npobject_lifetime_test") {
+ new_test = new NPObjectLifetimeTest(instance, host_functions);
+ } else if (test_name == "npobject_lifetime_test_second_instance") {
+ new_test = new NPObjectLifetimeTestInstance2(instance, host_functions);
+ } else if (test_name == "new_fails") {
+ new_test = new NewFailsTest(instance, host_functions);
+ } else if (test_name == "npobject_delete_plugin_in_evaluate" ||
+ test_name == "npobject_delete_create_plugin_in_evaluate") {
+ new_test = new NPObjectDeletePluginInNPN_Evaluate(instance, host_functions);
+#endif
+ } else if (test_name == "plugin_javascript_open_popup_with_plugin") {
+ new_test = new ExecuteJavascriptOpenPopupWithPluginTest(
+ instance, host_functions);
+ } else if (test_name == "plugin_popup_with_plugin_target") {
+ new_test = new ExecuteJavascriptPopupWindowTargetPluginTest(
+ instance, host_functions);
+ } else if (test_name == "plugin_thread_async_call") {
+ new_test = new PluginThreadAsyncCallTest(instance, host_functions);
+ } else if (test_name == "private") {
+ new_test = new PrivateTest(instance, host_functions);
+ } else if (test_name == "schedule_timer") {
+ new_test = new ScheduleTimerTest(instance, host_functions);
+#if defined(OS_WIN)
+ // TODO(port): plugin_windowed_test.*.
+ } else if (test_name == "hidden_plugin" ||
+ test_name == "create_instance_in_paint" ||
+ test_name == "alert_in_window_message" ||
+ test_name == "ensure_scripting_works_in_destroy" ||
+ test_name == "invoke_js_function_on_create") {
+ new_test = new WindowedPluginTest(instance, host_functions);
+#endif
+ } else if (test_name == "setup") {
+ // "plugin" is the name for plugin documents.
+ new_test = new PluginSetupTest(instance, host_functions);
+ }
+
+ return new_test;
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_test_factory.h b/webkit/glue/plugins/test/plugin_test_factory.h
new file mode 100644
index 0000000..3fd38d5
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_test_factory.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_PORT_PLUGINS_TEST_PLUGIN_TEST_FACTROY_H__
+#define WEBKIT_PORT_PLUGINS_TEST_PLUGIN_TEST_FACTROY_H__
+
+#include <string>
+
+#include "third_party/npapi/bindings/nphostapi.h"
+
+namespace NPAPIClient {
+
+class PluginTest;
+
+extern PluginTest* CreatePluginTest(const std::string& test_name,
+ NPP instance,
+ NPNetscapeFuncs* host_functions);
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_PORT_PLUGINS_TEST_PLUGIN_TEST_FACTROY_H__
diff --git a/webkit/glue/plugins/test/plugin_thread_async_call_test.cc b/webkit/glue/plugins/test/plugin_thread_async_call_test.cc
new file mode 100644
index 0000000..c01a49e
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_thread_async_call_test.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_thread_async_call_test.h"
+
+#include "base/at_exit.h"
+#include "base/message_loop.h"
+#include "base/thread.h"
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+namespace NPAPIClient {
+
+namespace {
+
+// There are two plugin instances in this test. The long lived instance is used
+// for reporting errors and signalling test completion. The short lived one is
+// used to verify that async callbacks are not invoked after NPP_Destroy.
+PluginThreadAsyncCallTest* g_short_lived_instance;
+PluginThreadAsyncCallTest* g_long_lived_instance;
+
+void OnCallSucceededHelper(void* data) {
+ static_cast<PluginThreadAsyncCallTest*>(data)->OnCallSucceeded();
+}
+
+class AsyncCallTask : public Task {
+ public:
+ AsyncCallTask(PluginThreadAsyncCallTest* test_class)
+ : test_class_(test_class) {}
+
+ void Run() {
+ test_class_->AsyncCall();
+ }
+
+ private:
+ PluginThreadAsyncCallTest* test_class_;
+};
+
+void OnCallFailed(void* data) {
+ g_long_lived_instance->SetError("Async callback invoked after NPP_Destroy");
+}
+
+void OnCallCompletedHelper(void* data) {
+ static_cast<PluginThreadAsyncCallTest*>(data)->OnCallCompleted();
+}
+}
+
+PluginThreadAsyncCallTest::PluginThreadAsyncCallTest(
+ NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions) {
+}
+
+NPError PluginThreadAsyncCallTest::New(
+ uint16 mode, int16 argc, const char* argn[], const char* argv[],
+ NPSavedData* saved) {
+ NPError error = PluginTest::New(mode, argc, argn, argv, saved);
+ if (error != NPERR_NO_ERROR)
+ return error;
+
+ // Determine whether this is the short lived instance.
+ for (int i = 0; i < argc; ++i) {
+ if (base::strcasecmp(argn[i], "short_lived") == 0) {
+ if (base::strcasecmp(argv[i], "true") == 0) {
+ g_short_lived_instance = this;
+ } else {
+ g_long_lived_instance = this;
+ }
+ }
+ }
+
+ // Schedule an async call that will succeed. Make sure to call that API from
+ // a different thread to fully test it.
+ if (this == g_short_lived_instance) {
+ at_exit_manager_.reset(new base::AtExitManager());
+ base::Thread random_thread("random_thread");
+ random_thread.Start();
+ random_thread.message_loop()->PostTask(FROM_HERE, new AsyncCallTask(this));
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+void PluginThreadAsyncCallTest::AsyncCall() {
+ HostFunctions()->pluginthreadasynccall(id(), OnCallSucceededHelper, this);
+}
+
+void PluginThreadAsyncCallTest::OnCallSucceeded() {
+ // Delete the short lived instance.
+ NPIdentifier delete_id = HostFunctions()->getstringidentifier(
+ "deleteShortLivedInstance");
+
+ NPObject *window_obj = NULL;
+ HostFunctions()->getvalue(id(), NPNVWindowNPObject, &window_obj);
+
+ NPVariant result;
+ HostFunctions()->invoke(id(), window_obj, delete_id, NULL, 0, &result);
+}
+
+NPError PluginThreadAsyncCallTest::Destroy() {
+ if (this == g_short_lived_instance) {
+ // Schedule an async call that should not be called.
+ HostFunctions()->pluginthreadasynccall(id(), OnCallFailed, NULL);
+
+ // Schedule an async call to end the test using the long lived instance.
+ HostFunctions()->pluginthreadasynccall(g_long_lived_instance->id(),
+ OnCallCompletedHelper,
+ g_long_lived_instance);
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+void PluginThreadAsyncCallTest::OnCallCompleted() {
+ SignalTestCompleted();
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_thread_async_call_test.h b/webkit/glue/plugins/test/plugin_thread_async_call_test.h
new file mode 100644
index 0000000..78e4e8d
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_thread_async_call_test.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_THREAD_ASYNC_CALL_TEST_H_
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_THREAD_ASYNC_CALL_TEST_H_
+
+#include "base/scoped_ptr.h"
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace base {
+class AtExitManager;
+}
+
+namespace NPAPIClient {
+
+// This class tests scheduling and unscheduling of async callbacks using
+// NPN_PluginThreadAsyncCall.
+class PluginThreadAsyncCallTest : public PluginTest {
+ public:
+ PluginThreadAsyncCallTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ virtual NPError New(uint16 mode, int16 argc, const char* argn[],
+ const char* argv[], NPSavedData* saved);
+
+ virtual NPError Destroy();
+
+ void AsyncCall();
+ void OnCallSucceeded();
+ void OnCallCompleted();
+
+ private:
+ // base::Thread needs one of these.
+ scoped_ptr<base::AtExitManager> at_exit_manager_;
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_THREAD_ASYNC_CALL_TEST_H_
diff --git a/webkit/glue/plugins/test/plugin_window_size_test.cc b/webkit/glue/plugins/test/plugin_window_size_test.cc
new file mode 100644
index 0000000..9bfabca
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_window_size_test.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_window_size_test.h"
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+namespace NPAPIClient {
+
+PluginWindowSizeTest::PluginWindowSizeTest(NPP id,
+ NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions) {
+}
+
+NPError PluginWindowSizeTest::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ HWND window = reinterpret_cast<HWND>(pNPWindow->window);
+ if (!pNPWindow || !::IsWindow(window)) {
+ SetError("Invalid arguments passed in");
+ return NPERR_INVALID_PARAM;
+ }
+
+ RECT window_rect = {0};
+ window_rect.left = pNPWindow->x;
+ window_rect.top = pNPWindow->y;
+ window_rect.right = pNPWindow->width;
+ window_rect.bottom = pNPWindow->height;
+
+ if (!::IsRectEmpty(&window_rect)) {
+ RECT client_rect = {0};
+ ::GetClientRect(window, &client_rect);
+ if (::IsRectEmpty(&client_rect)) {
+ SetError("The client rect of the plugin window is empty. Test failed");
+ }
+
+ // Bug 6742: ensure that the coordinates passed in are relative to the
+ // parent HWND.
+ POINT origin_from_os;
+ RECT window_rect_from_os;
+ ::GetWindowRect(window, &window_rect_from_os);
+ origin_from_os.x = window_rect_from_os.left;
+ origin_from_os.y = window_rect_from_os.top;
+ ::ScreenToClient(GetParent(window), &origin_from_os);
+ if (origin_from_os.x != pNPWindow->x || origin_from_os.y != pNPWindow->y)
+ SetError("Wrong position passed in to SetWindow! Test failed");
+
+ SignalTestCompleted();
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_window_size_test.h b/webkit/glue/plugins/test/plugin_window_size_test.h
new file mode 100644
index 0000000..3650671
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_window_size_test.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_WINDOW_SIZE_TEST_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_WINDOW_SIZE_TEST_H
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class tests whether the plugin window has a non zero rect
+// on the second SetWindow call.
+class PluginWindowSizeTest : public PluginTest {
+ public:
+ // Constructor.
+ PluginWindowSizeTest(NPP id, NPNetscapeFuncs *host_functions);
+ // NPAPI SetWindow handler
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_WINDOW_SIZE_TEST_H
diff --git a/webkit/glue/plugins/test/plugin_windowed_test.cc b/webkit/glue/plugins/test/plugin_windowed_test.cc
new file mode 100644
index 0000000..c82aa55
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_windowed_test.cc
@@ -0,0 +1,150 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/test/plugin_windowed_test.h"
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+namespace NPAPIClient {
+
+WindowedPluginTest::WindowedPluginTest(NPP id, NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions),
+ window_(NULL), done_(false) {
+}
+
+WindowedPluginTest::~WindowedPluginTest() {
+ if (window_)
+ DestroyWindow(window_);
+}
+
+NPError WindowedPluginTest::SetWindow(NPWindow* pNPWindow) {
+ if (pNPWindow->window == NULL)
+ return NPERR_NO_ERROR;
+
+ if (test_name() == "create_instance_in_paint" && test_id() == "2") {
+ SignalTestCompleted();
+ return NPERR_NO_ERROR;
+ }
+
+ if (window_)
+ return NPERR_NO_ERROR;
+
+ HWND parent = reinterpret_cast<HWND>(pNPWindow->window);
+ if (!pNPWindow || !::IsWindow(parent)) {
+ SetError("Invalid arguments passed in");
+ return NPERR_INVALID_PARAM;
+ }
+
+ if ((test_name() == "create_instance_in_paint" && test_id() == "1") ||
+ test_name() == "alert_in_window_message" ||
+ test_name() == "invoke_js_function_on_create") {
+ static ATOM window_class = 0;
+ if (!window_class) {
+ WNDCLASSEX wcex;
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.style = CS_DBLCLKS;
+ wcex.lpfnWndProc = &NPAPIClient::WindowedPluginTest::WindowProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = GetModuleHandle(NULL);
+ wcex.hIcon = 0;
+ wcex.hCursor = 0;
+ wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1);
+ wcex.lpszMenuName = 0;
+ wcex.lpszClassName = L"CreateInstanceInPaintTestWindowClass";
+ wcex.hIconSm = 0;
+ window_class = RegisterClassEx(&wcex);
+ }
+
+ window_ = CreateWindowEx(
+ WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR,
+ MAKEINTATOM(window_class), 0,
+ WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE ,
+ 0, 0, 100, 100, parent, 0, GetModuleHandle(NULL), 0);
+ DCHECK(window_);
+ // TODO: this propery leaks.
+ ::SetProp(window_, L"Plugin_Instance", this);
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+NPError WindowedPluginTest::Destroy() {
+ if (test_name() != "ensure_scripting_works_in_destroy")
+ return NPERR_NO_ERROR;
+
+ // Bug 23706: ensure that scripting works with no asserts.
+ NPObject *window_obj = NULL;
+ HostFunctions()->getvalue(id(), NPNVWindowNPObject,&window_obj);
+
+ if (!window_obj) {
+ SetError("Failed to get NPObject for plugin instance");
+ } else {
+ std::string script = "javascript:GetMagicNumber()";
+ NPString script_string;
+ script_string.UTF8Characters = script.c_str();
+ script_string.UTF8Length =
+ static_cast<unsigned int>(script.length());
+
+ NPVariant result_var;
+ bool result = HostFunctions()->evaluate(
+ id(), window_obj, &script_string, &result_var);
+ if (!result ||
+ result_var.type != NPVariantType_Double ||
+ result_var.value.doubleValue != 42.0) {
+ SetError("Failed to script during NPP_Destroy");
+ }
+ }
+
+ SignalTestCompleted();
+ return NPERR_NO_ERROR;
+}
+
+void WindowedPluginTest::CallJSFunction(
+ WindowedPluginTest* this_ptr, const char* function) {
+ NPIdentifier function_id = this_ptr->HostFunctions()->getstringidentifier(
+ function);
+
+ NPObject *window_obj = NULL;
+ this_ptr->HostFunctions()->getvalue(
+ this_ptr->id(), NPNVWindowNPObject, &window_obj);
+
+ NPVariant rv;
+ this_ptr->HostFunctions()->invoke(
+ this_ptr->id(), window_obj, function_id, NULL, 0, &rv);
+}
+
+LRESULT CALLBACK WindowedPluginTest::WindowProc(
+ HWND window, UINT message, WPARAM wparam, LPARAM lparam) {
+ WindowedPluginTest* this_ptr =
+ reinterpret_cast<WindowedPluginTest*>
+ (::GetProp(window, L"Plugin_Instance"));
+
+ if (this_ptr && !this_ptr->done_) {
+ if (this_ptr->test_name() == "create_instance_in_paint" &&
+ message == WM_PAINT) {
+ this_ptr->done_ = true;
+ CallJSFunction(this_ptr, "CreateNewInstance");
+ } else if (this_ptr->test_name() == "alert_in_window_message" &&
+ message == WM_PAINT) {
+ this_ptr->done_ = true;
+ // We call this function twice as we want to display two alerts
+ // and verify that we don't hang the browser.
+ CallJSFunction(this_ptr, "CallAlert");
+ CallJSFunction(this_ptr, "CallAlert");
+ } else if (this_ptr->test_name() ==
+ "invoke_js_function_on_create" &&
+ message == WM_PAINT) {
+ this_ptr->done_ = true;
+ CallJSFunction(this_ptr, "PluginCreated");
+ }
+
+ if (this_ptr->done_) {
+ ::RemoveProp(window, L"Plugin_Instance");
+ }
+ }
+
+ return DefWindowProc(window, message, wparam, lparam);
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_windowed_test.h b/webkit/glue/plugins/test/plugin_windowed_test.h
new file mode 100644
index 0000000..949ea86
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_windowed_test.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_WINDOWED_TEST_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_WINDOWED_TEST_H
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class contains a list of windowed plugin tests. Please add additional
+// tests to this class.
+class WindowedPluginTest : public PluginTest {
+ public:
+ WindowedPluginTest(NPP id, NPNetscapeFuncs *host_functions);
+ ~WindowedPluginTest();
+
+ private:
+ static LRESULT CALLBACK WindowProc(
+ HWND window, UINT message, WPARAM wparam, LPARAM lparam);
+ static void CallJSFunction(WindowedPluginTest*, const char*);
+
+ virtual NPError SetWindow(NPWindow* pNPWindow);
+ virtual NPError Destroy();
+
+ HWND window_;
+ bool done_;
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_WINDOWED_TEST_H
diff --git a/webkit/glue/plugins/test/plugin_windowless_test.cc b/webkit/glue/plugins/test/plugin_windowless_test.cc
new file mode 100644
index 0000000..aa6a9d7
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_windowless_test.cc
@@ -0,0 +1,261 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#define STRSAFE_NO_DEPRECATE
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "webkit/glue/plugins/test/plugin_windowless_test.h"
+#include "webkit/glue/plugins/test/plugin_client.h"
+
+#if defined(OS_MACOSX)
+#include <ApplicationServices/ApplicationServices.h>
+#include <Carbon/Carbon.h>
+#endif
+
+namespace NPAPIClient {
+
+// Remember the first plugin instance for tests involving multiple instances
+WindowlessPluginTest* g_other_instance = NULL;
+
+WindowlessPluginTest::WindowlessPluginTest(NPP id,
+ NPNetscapeFuncs *host_functions)
+ : PluginTest(id, host_functions) {
+ if (!g_other_instance)
+ g_other_instance = this;
+}
+
+static bool IsPaintEvent(NPEvent* np_event) {
+#if defined(OS_WIN)
+ return WM_PAINT == np_event->event;
+#elif defined(OS_MACOSX)
+ return np_event->what == updateEvt;
+#endif
+}
+
+static bool IsMouseMoveEvent(NPEvent* np_event) {
+#if defined(OS_WIN)
+ return WM_MOUSEMOVE == np_event->event;
+#elif defined(OS_MACOSX)
+ return np_event->what == nullEvent;
+#endif
+}
+
+static bool IsMouseUpEvent(NPEvent* np_event) {
+#if defined(OS_WIN)
+ return WM_LBUTTONUP == np_event->event;
+#elif defined(OS_MACOSX)
+ return np_event->what == mouseUp;
+#endif
+}
+
+static bool IsWindowActivationEvent(NPEvent* np_event) {
+#if defined(OS_WIN)
+ NOTIMPLEMENTED();
+ return false;
+#elif defined(OS_MACOSX)
+ return np_event->what == activateEvt;
+#endif
+}
+
+int16 WindowlessPluginTest::HandleEvent(void* event) {
+
+ NPNetscapeFuncs* browser = NPAPIClient::PluginClient::HostFunctions();
+
+ NPBool supports_windowless = 0;
+ NPError result = browser->getvalue(id(), NPNVSupportsWindowless,
+ &supports_windowless);
+ if ((result != NPERR_NO_ERROR) || (supports_windowless != TRUE)) {
+ SetError("Failed to read NPNVSupportsWindowless value");
+ SignalTestCompleted();
+ return PluginTest::HandleEvent(event);
+ }
+
+ NPEvent* np_event = reinterpret_cast<NPEvent*>(event);
+ if (IsPaintEvent(np_event)) {
+#if defined(OS_WIN)
+ HDC paint_dc = reinterpret_cast<HDC>(np_event->wParam);
+ if (paint_dc == NULL) {
+ SetError("Invalid Window DC passed to HandleEvent for WM_PAINT");
+ SignalTestCompleted();
+ return NPERR_GENERIC_ERROR;
+ }
+
+ HRGN clipping_region = CreateRectRgn(0, 0, 0, 0);
+ if (!GetClipRgn(paint_dc, clipping_region)) {
+ SetError("No clipping region set in window DC");
+ DeleteObject(clipping_region);
+ SignalTestCompleted();
+ return NPERR_GENERIC_ERROR;
+ }
+
+ DeleteObject(clipping_region);
+#endif
+
+ if (test_name() == "execute_script_delete_in_paint") {
+ ExecuteScriptDeleteInPaint(browser);
+ } else if (test_name() == "multiple_instances_sync_calls") {
+ MultipleInstanceSyncCalls(browser);
+ }
+#if OS_MACOSX
+ } else if (IsWindowActivationEvent(np_event) &&
+ test_name() == "convert_point") {
+ ConvertPoint(browser);
+#endif
+ } else if (IsMouseMoveEvent(np_event) &&
+ test_name() == "execute_script_delete_in_mouse_move") {
+ ExecuteScript(browser, id(), "DeletePluginWithinScript();", NULL);
+ SignalTestCompleted();
+ } else if (IsMouseUpEvent(np_event) &&
+ test_name() == "delete_frame_test") {
+ ExecuteScript(
+ browser, id(),
+ "parent.document.getElementById('frame').outerHTML = ''", NULL);
+ }
+ // If this test failed, then we'd have crashed by now.
+ return PluginTest::HandleEvent(event);
+}
+
+NPError WindowlessPluginTest::ExecuteScript(NPNetscapeFuncs* browser, NPP id,
+ const std::string& script, NPVariant* result) {
+ std::string script_url = "javascript:";
+ script_url += script;
+
+ NPString script_string = { script_url.c_str(), script_url.length() };
+ NPObject *window_obj = NULL;
+ browser->getvalue(id, NPNVWindowNPObject, &window_obj);
+
+ NPVariant unused_result;
+ if (!result)
+ result = &unused_result;
+
+ return browser->evaluate(id, window_obj, &script_string, result);
+}
+
+void WindowlessPluginTest::ExecuteScriptDeleteInPaint(
+ NPNetscapeFuncs* browser) {
+ const NPUTF8* urlString = "javascript:DeletePluginWithinScript()";
+ const NPUTF8* targetString = NULL;
+ browser->geturl(id(), urlString, targetString);
+ SignalTestCompleted();
+}
+
+void WindowlessPluginTest::MultipleInstanceSyncCalls(NPNetscapeFuncs* browser) {
+ if (this == g_other_instance)
+ return;
+
+ DCHECK(g_other_instance);
+ ExecuteScript(browser, g_other_instance->id(), "TestCallback();", NULL);
+ SignalTestCompleted();
+}
+
+#if defined(OS_MACOSX)
+std::string StringForPoint(int x, int y) {
+ std::string point_string("(");
+ point_string.append(base::IntToString(x));
+ point_string.append(", ");
+ point_string.append(base::IntToString(y));
+ point_string.append(")");
+ return point_string;
+}
+#endif
+
+void WindowlessPluginTest::ConvertPoint(NPNetscapeFuncs* browser) {
+#if defined(OS_MACOSX)
+ // First, just sanity-test that round trips work.
+ NPCoordinateSpace spaces[] = { NPCoordinateSpacePlugin,
+ NPCoordinateSpaceWindow,
+ NPCoordinateSpaceFlippedWindow,
+ NPCoordinateSpaceScreen,
+ NPCoordinateSpaceFlippedScreen };
+ for (unsigned int i = 0; i < arraysize(spaces); ++i) {
+ for (unsigned int j = 0; j < arraysize(spaces); ++j) {
+ double x, y, round_trip_x, round_trip_y;
+ if (!(browser->convertpoint(id(), 0, 0, spaces[i], &x, &y, spaces[j])) ||
+ !(browser->convertpoint(id(), x, y, spaces[j], &round_trip_x,
+ &round_trip_y, spaces[i]))) {
+ SetError("Conversion failed");
+ SignalTestCompleted();
+ return;
+ }
+ if (i != j && x == 0 && y == 0) {
+ SetError("Converting a coordinate should change it");
+ SignalTestCompleted();
+ return;
+ }
+ if (round_trip_x != 0 || round_trip_y != 0) {
+ SetError("Round-trip conversion should return the original point");
+ SignalTestCompleted();
+ return;
+ }
+ }
+ }
+
+ // Now, more extensive testing on a single point.
+ double screen_x, screen_y;
+ browser->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin,
+ &screen_x, &screen_y, NPCoordinateSpaceScreen);
+ double flipped_screen_x, flipped_screen_y;
+ browser->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin,
+ &flipped_screen_x, &flipped_screen_y,
+ NPCoordinateSpaceFlippedScreen);
+ double window_x, window_y;
+ browser->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin,
+ &window_x, &window_y, NPCoordinateSpaceWindow);
+ double flipped_window_x, flipped_window_y;
+ browser->convertpoint(id(), 0, 0, NPCoordinateSpacePlugin,
+ &flipped_window_x, &flipped_window_y,
+ NPCoordinateSpaceFlippedWindow);
+
+ CGRect main_display_bounds = CGDisplayBounds(CGMainDisplayID());
+
+ // Check that all the coordinates are right. The constants below are based on
+ // the window frame set in the UI test and the content offset in the test
+ // html. Y-coordinates are not checked exactly so that the test is robust
+ // against toolbar changes, info and bookmark bar visibility, etc.
+ const int kWindowHeight = 400;
+ const int kWindowXOrigin = 50;
+ const int kWindowYOrigin = 50;
+ const int kPluginXContentOffset = 50;
+ const int kPluginYContentOffset = 50;
+ const int kChromeYTolerance = 200;
+
+ std::string error_string;
+ if (screen_x != flipped_screen_x)
+ error_string = "Flipping screen coordinates shouldn't change x";
+ else if (flipped_screen_y != main_display_bounds.size.height - screen_y)
+ error_string = "Flipped screen coordinates should be flipped vertically";
+ else if (screen_x != kWindowXOrigin + kPluginXContentOffset)
+ error_string = "Screen x location is wrong";
+ else if (flipped_screen_y < kWindowYOrigin + kPluginYContentOffset ||
+ flipped_screen_y > kWindowYOrigin + kPluginYContentOffset +
+ kChromeYTolerance)
+ error_string = "Screen y location is wrong";
+ else if (window_x != flipped_window_x)
+ error_string = "Flipping window coordinates shouldn't change x";
+ else if (flipped_window_y != kWindowHeight - window_y)
+ error_string = "Flipped window coordinates should be flipped vertically";
+ else if (window_x != kPluginXContentOffset)
+ error_string = "Window x location is wrong";
+ else if (flipped_window_y < kPluginYContentOffset ||
+ flipped_window_y > kPluginYContentOffset + kChromeYTolerance)
+ error_string = "Window y location is wrong";
+
+ if (!error_string.empty()) {
+ error_string.append(" - ");
+ error_string.append(StringForPoint(screen_x, screen_y));
+ error_string.append(" - ");
+ error_string.append(StringForPoint(flipped_screen_x, flipped_screen_y));
+ error_string.append(" - ");
+ error_string.append(StringForPoint(window_x, window_y));
+ error_string.append(" - ");
+ error_string.append(StringForPoint(flipped_window_x, flipped_window_y));
+ SetError(error_string);
+ }
+#else
+ SetError("Unimplemented");
+#endif
+ SignalTestCompleted();
+}
+
+} // namespace NPAPIClient
diff --git a/webkit/glue/plugins/test/plugin_windowless_test.h b/webkit/glue/plugins/test/plugin_windowless_test.h
new file mode 100644
index 0000000..f336653
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_windowless_test.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_EXECUTE_SCRIPT_DELETE_TEST_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_EXECUTE_SCRIPT_DELETE_TEST_H
+
+#include "webkit/glue/plugins/test/plugin_test.h"
+
+namespace NPAPIClient {
+
+// This class contains a list of windowless plugin tests. Please add additional
+// tests to this class.
+class WindowlessPluginTest : public PluginTest {
+ public:
+ // Constructor.
+ WindowlessPluginTest(NPP id, NPNetscapeFuncs *host_functions);
+
+ // These tests run in windowless plugin mode.
+ virtual bool IsWindowless() const { return true; }
+
+ // NPAPI HandleEvent handler
+ virtual int16 HandleEvent(void* event);
+
+ protected:
+ NPError ExecuteScript(NPNetscapeFuncs* browser, NPP id,
+ const std::string& script, NPVariant* result);
+ void ExecuteScriptDeleteInPaint(NPNetscapeFuncs* browser);
+ void MultipleInstanceSyncCalls(NPNetscapeFuncs* browser);
+ void ConvertPoint(NPNetscapeFuncs* browser);
+};
+
+} // namespace NPAPIClient
+
+#endif // WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_EXECUTE_SCRIPT_DELETE_TEST_H
diff --git a/webkit/glue/plugins/test/resource.h b/webkit/glue/plugins/test/resource.h
new file mode 100644
index 0000000..c52fa82
--- /dev/null
+++ b/webkit/glue/plugins/test/resource.h
@@ -0,0 +1,15 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by npapi_test.rc
+//
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/webkit/glue/plugins/url_request_info_unittest.cc b/webkit/glue/plugins/url_request_info_unittest.cc
new file mode 100644
index 0000000..341eeb2
--- /dev/null
+++ b/webkit/glue/plugins/url_request_info_unittest.cc
@@ -0,0 +1,249 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrameClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+
+#include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
+#include "webkit/plugins/ppapi/ppb_url_request_info_impl.h"
+#include "webkit/plugins/ppapi/ppapi_unittest.h"
+
+using WebKit::WebCString;
+using WebKit::WebFrame;
+using WebKit::WebFrameClient;
+using WebKit::WebString;
+using WebKit::WebView;
+using WebKit::WebURL;
+using WebKit::WebURLRequest;
+
+namespace {
+
+bool IsExpected(const WebCString& web_string, const char* expected) {
+ const char* result = web_string.data();
+ return strcmp(result, expected) == 0;
+}
+
+bool IsExpected(const WebString& web_string, const char* expected) {
+ return IsExpected(web_string.utf8(), expected);
+}
+
+bool IsNullOrEmpty(const WebString& web_string) {
+ return web_string.isNull() || web_string.isEmpty();
+}
+
+// The base class destructor is protected, so derive.
+class TestWebFrameClient : public WebFrameClient {
+};
+
+} // namespace
+
+namespace webkit {
+namespace ppapi {
+
+class URLRequestInfoTest : public PpapiUnittest {
+ public:
+ URLRequestInfoTest() : info_(new PPB_URLRequestInfo_Impl(module())) {
+ }
+
+ static void SetUpTestCase() {
+ web_view_ = WebView::create(0, 0);
+ web_view_->initializeMainFrame(&web_frame_client_);
+ WebURL web_url(GURL(""));
+ WebURLRequest url_request;
+ url_request.initialize();
+ url_request.setURL(web_url);
+ frame_ = web_view_->mainFrame();
+ frame_->loadRequest(url_request);
+ }
+
+ static void TearDownTestCase() {
+ web_view_->close();
+ }
+
+ bool GetDownloadToFile() {
+ WebURLRequest web_request = info_->ToWebURLRequest(frame_);
+ return web_request.downloadToFile();
+ }
+
+ WebCString GetURL() {
+ WebURLRequest web_request = info_->ToWebURLRequest(frame_);
+ return web_request.url().spec();
+ }
+
+ WebString GetMethod() {
+ WebURLRequest web_request = info_->ToWebURLRequest(frame_);
+ return web_request.httpMethod();
+ }
+
+ WebString GetHeaderValue(const char* field) {
+ WebURLRequest web_request = info_->ToWebURLRequest(frame_);
+ return web_request.httpHeaderField(WebString::fromUTF8(field));
+ }
+
+ scoped_refptr<PPB_URLRequestInfo_Impl> info_;
+
+ static TestWebFrameClient web_frame_client_;
+ static WebView* web_view_;
+ static WebFrame* frame_;
+};
+
+TestWebFrameClient URLRequestInfoTest::web_frame_client_;
+WebView* URLRequestInfoTest::web_view_;
+WebFrame* URLRequestInfoTest::frame_;
+
+TEST_F(URLRequestInfoTest, GetInterface) {
+ const PPB_URLRequestInfo* interface = info_->GetInterface();
+ ASSERT_TRUE(interface);
+ ASSERT_TRUE(interface->Create);
+ ASSERT_TRUE(interface->IsURLRequestInfo);
+ ASSERT_TRUE(interface->SetProperty);
+ ASSERT_TRUE(interface->AppendDataToBody);
+ ASSERT_TRUE(interface->AppendFileToBody);
+ ASSERT_TRUE(interface->Create);
+ ASSERT_TRUE(interface->Create);
+}
+
+TEST_F(URLRequestInfoTest, AsURLRequestInfo) {
+ ASSERT_EQ(info_, info_->AsPPB_URLRequestInfo_Impl());
+}
+
+TEST_F(URLRequestInfoTest, StreamToFile) {
+ info_->SetStringProperty(PP_URLREQUESTPROPERTY_URL, "http://www.google.com");
+
+ ASSERT_FALSE(GetDownloadToFile());
+
+ ASSERT_TRUE(info_->SetBooleanProperty(
+ PP_URLREQUESTPROPERTY_STREAMTOFILE, true));
+ ASSERT_TRUE(GetDownloadToFile());
+
+ ASSERT_TRUE(info_->SetBooleanProperty(
+ PP_URLREQUESTPROPERTY_STREAMTOFILE, false));
+ ASSERT_FALSE(GetDownloadToFile());
+}
+
+TEST_F(URLRequestInfoTest, FollowRedirects) {
+ ASSERT_TRUE(info_->follow_redirects());
+
+ ASSERT_TRUE(info_->SetBooleanProperty(
+ PP_URLREQUESTPROPERTY_FOLLOWREDIRECTS, false));
+ ASSERT_FALSE(info_->follow_redirects());
+
+ ASSERT_TRUE(info_->SetBooleanProperty(
+ PP_URLREQUESTPROPERTY_FOLLOWREDIRECTS, true));
+ ASSERT_TRUE(info_->follow_redirects());
+}
+
+TEST_F(URLRequestInfoTest, RecordDownloadProgress) {
+ ASSERT_FALSE(info_->record_download_progress());
+
+ ASSERT_TRUE(info_->SetBooleanProperty(
+ PP_URLREQUESTPROPERTY_RECORDDOWNLOADPROGRESS, true));
+ ASSERT_TRUE(info_->record_download_progress());
+
+ ASSERT_TRUE(info_->SetBooleanProperty(
+ PP_URLREQUESTPROPERTY_RECORDDOWNLOADPROGRESS, false));
+ ASSERT_FALSE(info_->record_download_progress());
+}
+
+TEST_F(URLRequestInfoTest, RecordUploadProgress) {
+ ASSERT_FALSE(info_->record_upload_progress());
+
+ ASSERT_TRUE(info_->SetBooleanProperty(
+ PP_URLREQUESTPROPERTY_RECORDUPLOADPROGRESS, true));
+ ASSERT_TRUE(info_->record_upload_progress());
+
+ ASSERT_TRUE(info_->SetBooleanProperty(
+ PP_URLREQUESTPROPERTY_RECORDUPLOADPROGRESS, false));
+ ASSERT_FALSE(info_->record_upload_progress());
+}
+
+TEST_F(URLRequestInfoTest, SetURL) {
+ // Test default URL is "about:blank".
+ ASSERT_TRUE(IsExpected(GetURL(), "about:blank"));
+
+ const char* url = "http://www.google.com/";
+ ASSERT_TRUE(info_->SetStringProperty(
+ PP_URLREQUESTPROPERTY_URL, url));
+ ASSERT_TRUE(IsExpected(GetURL(), url));
+}
+
+TEST_F(URLRequestInfoTest, SetMethod) {
+ // Test default method is "GET".
+ ASSERT_TRUE(IsExpected(GetMethod(), "GET"));
+ ASSERT_TRUE(info_->SetStringProperty(
+ PP_URLREQUESTPROPERTY_METHOD, "POST"));
+ ASSERT_TRUE(IsExpected(GetMethod(), "POST"));
+}
+
+TEST_F(URLRequestInfoTest, SetValidHeaders) {
+ // Test default header field.
+ ASSERT_TRUE(IsExpected(
+ GetHeaderValue("foo"), ""));
+ // Test that we can set a header field.
+ ASSERT_TRUE(info_->SetStringProperty(
+ PP_URLREQUESTPROPERTY_HEADERS, "foo: bar"));
+ ASSERT_TRUE(IsExpected(
+ GetHeaderValue("foo"), "bar"));
+ // Test that we can set multiple header fields using \n delimiter.
+ ASSERT_TRUE(info_->SetStringProperty(
+ PP_URLREQUESTPROPERTY_HEADERS, "foo: bar\nbar: baz"));
+ ASSERT_TRUE(IsExpected(
+ GetHeaderValue("foo"), "bar"));
+ ASSERT_TRUE(IsExpected(
+ GetHeaderValue("bar"), "baz"));
+}
+
+TEST_F(URLRequestInfoTest, SetInvalidHeaders) {
+ const char* const kForbiddenHeaderFields[] = {
+ "accept-charset",
+ "accept-encoding",
+ "connection",
+ "content-length",
+ "cookie",
+ "cookie2",
+ "content-transfer-encoding",
+ "date",
+ "expect",
+ "host",
+ "keep-alive",
+ "origin",
+ "referer",
+ "te",
+ "trailer",
+ "transfer-encoding",
+ "upgrade",
+ "user-agent",
+ "via",
+
+ "proxy-foo", // Test for any header starting with proxy- or sec-.
+ "sec-foo",
+ };
+
+ // Test that no forbidden header fields can be set.
+ for (size_t i = 0; i < arraysize(kForbiddenHeaderFields); ++i) {
+ std::string headers(kForbiddenHeaderFields[i]);
+ headers.append(": foo");
+ ASSERT_FALSE(info_->SetStringProperty(
+ PP_URLREQUESTPROPERTY_HEADERS, headers.c_str()));
+ ASSERT_TRUE(IsNullOrEmpty(GetHeaderValue(kForbiddenHeaderFields[i])));
+ }
+
+ // Test that forbidden header can't be set in various ways.
+ ASSERT_FALSE(info_->SetStringProperty(
+ PP_URLREQUESTPROPERTY_HEADERS, "cookie : foo"));
+ ASSERT_TRUE(IsNullOrEmpty(GetHeaderValue("cookie")));
+
+ // Test that forbidden header can't be set with an allowed one.
+ ASSERT_FALSE(info_->SetStringProperty(
+ PP_URLREQUESTPROPERTY_HEADERS, "foo: bar\ncookie: foo"));
+ ASSERT_TRUE(IsNullOrEmpty(GetHeaderValue("cookie")));
+}
+
+// TODO(bbudge) Unit tests for AppendDataToBody, AppendFileToBody.
+
+} // namespace ppapi
+} // namespace webkit
+
diff --git a/webkit/glue/plugins/webplugin.cc b/webkit/glue/plugins/webplugin.cc
new file mode 100644
index 0000000..f780e18f
--- /dev/null
+++ b/webkit/glue/plugins/webplugin.cc
@@ -0,0 +1,31 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webplugin.h"
+
+namespace webkit_glue {
+
+WebPluginGeometry::WebPluginGeometry()
+ : window(gfx::kNullPluginWindow),
+ rects_valid(false),
+ visible(false) {
+}
+
+WebPluginGeometry::~WebPluginGeometry() {
+}
+
+bool WebPluginGeometry::Equals(const WebPluginGeometry& rhs) const {
+ return window == rhs.window &&
+ window_rect == rhs.window_rect &&
+ clip_rect == rhs.clip_rect &&
+ cutout_rects == rhs.cutout_rects &&
+ rects_valid == rhs.rects_valid &&
+ visible == rhs.visible;
+}
+
+WebPluginDelegate* WebPlugin::delegate() {
+ return NULL;
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/plugins/webplugin.h b/webkit/glue/plugins/webplugin.h
new file mode 100644
index 0000000..8a61027
--- /dev/null
+++ b/webkit/glue/plugins/webplugin.h
@@ -0,0 +1,200 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBPLUGIN_H_
+#define WEBKIT_GLUE_WEBPLUGIN_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "gfx/native_widget_types.h"
+#include "gfx/rect.h"
+
+// TODO(port): this typedef is obviously incorrect on non-Windows
+// platforms, but now a lot of code now accidentally depends on them
+// existing. #ifdef out these declarations and fix all the users.
+typedef void* HANDLE;
+
+class GURL;
+struct NPObject;
+
+namespace WebKit {
+class WebFrame;
+}
+
+namespace webkit_glue {
+
+class WebPluginDelegate;
+class WebPluginParentView;
+class WebPluginResourceClient;
+#if defined(OS_MACOSX)
+class WebPluginAcceleratedSurface;
+#endif
+
+// Describes the new location for a plugin window.
+struct WebPluginGeometry {
+ WebPluginGeometry();
+ ~WebPluginGeometry();
+
+ bool Equals(const WebPluginGeometry& rhs) const;
+
+ // On Windows, this is the plugin window in the plugin process.
+ // On X11, this is the XID of the plugin-side GtkPlug containing the
+ // GtkSocket hosting the actual plugin window.
+ //
+ // On Mac OS X, all of the plugin types are currently "windowless"
+ // (window == 0) except for the special case of the GPU plugin,
+ // which currently performs rendering on behalf of the Pepper 3D API
+ // and WebGL. The GPU plugin uses a simple integer for the
+ // PluginWindowHandle which is used to map to a side data structure
+ // containing information about the plugin. Soon this plugin will be
+ // generalized, at which point this mechanism will be rethought or
+ // removed.
+ gfx::PluginWindowHandle window;
+ gfx::Rect window_rect;
+ // Clip rect (include) and cutouts (excludes), relative to
+ // window_rect origin.
+ gfx::Rect clip_rect;
+ std::vector<gfx::Rect> cutout_rects;
+ bool rects_valid;
+ bool visible;
+};
+
+// The WebKit side of a plugin implementation. It provides wrappers around
+// operations that need to interact with the frame and other WebCore objects.
+class WebPlugin {
+ public:
+ virtual ~WebPlugin() {}
+
+ // Called by the plugin delegate to let the WebPlugin know if the plugin is
+ // windowed (i.e. handle is not NULL) or windowless (handle is NULL). This
+ // tells the WebPlugin to send mouse/keyboard events to the plugin delegate,
+ // as well as the information about the HDC for paint operations.
+ virtual void SetWindow(gfx::PluginWindowHandle window) = 0;
+
+ // Whether input events should be sent to the delegate.
+ virtual void SetAcceptsInputEvents(bool accepts) = 0;
+
+ // Called by the plugin delegate to let it know that the window is being
+ // destroyed.
+ virtual void WillDestroyWindow(gfx::PluginWindowHandle window) = 0;
+#if defined(OS_WIN)
+ // The pump_messages_event is a event handle which is valid only for
+ // windowless plugins and is used in NPP_HandleEvent calls to pump messages
+ // if the plugin enters a modal loop.
+ // Cancels a pending request.
+ virtual void SetWindowlessPumpEvent(HANDLE pump_messages_event) = 0;
+#endif
+ virtual void CancelResource(unsigned long id) = 0;
+ virtual void Invalidate() = 0;
+ virtual void InvalidateRect(const gfx::Rect& rect) = 0;
+
+ // Returns the NPObject for the browser's window object.
+ virtual NPObject* GetWindowScriptNPObject() = 0;
+
+ // Returns the DOM element that loaded the plugin.
+ virtual NPObject* GetPluginElement() = 0;
+
+ // Cookies
+ virtual void SetCookie(const GURL& url,
+ const GURL& first_party_for_cookies,
+ const std::string& cookie) = 0;
+ virtual std::string GetCookies(const GURL& url,
+ const GURL& first_party_for_cookies) = 0;
+
+ // Shows a modal HTML dialog containing the given URL. json_arguments are
+ // passed to the dialog via the DOM 'window.chrome.dialogArguments', and the
+ // retval is the string returned by 'window.chrome.send("DialogClose",
+ // retval)'.
+ virtual void ShowModalHTMLDialog(const GURL& url, int width, int height,
+ const std::string& json_arguments,
+ std::string* json_retval) = 0;
+
+ // When a default plugin has downloaded the plugin list and finds it is
+ // available, it calls this method to notify the renderer. Also it will update
+ // the status when user clicks on the plugin to install.
+ virtual void OnMissingPluginStatus(int status) = 0;
+
+ // Handles GetURL/GetURLNotify/PostURL/PostURLNotify requests initiated
+ // by plugins. If the plugin wants notification of the result, notify_id will
+ // be non-zero.
+ virtual void HandleURLRequest(const char* url,
+ const char* method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ int notify_id,
+ bool popups_allowed,
+ bool notify_redirects) = 0;
+
+ // Cancels document load.
+ virtual void CancelDocumentLoad() = 0;
+
+ // Initiates a HTTP range request for an existing stream.
+ virtual void InitiateHTTPRangeRequest(const char* url,
+ const char* range_info,
+ int range_request_id) = 0;
+
+ // Returns true iff in off the record (Incognito) mode.
+ virtual bool IsOffTheRecord() = 0;
+
+ // Called when the WebPluginResourceClient instance is deleted.
+ virtual void ResourceClientDeleted(
+ WebPluginResourceClient* resource_client) {}
+
+ // Defers the loading of the resource identified by resource_id. This is
+ // controlled by the defer parameter.
+ virtual void SetDeferResourceLoading(unsigned long resource_id,
+ bool defer) = 0;
+
+#if defined(OS_MACOSX)
+ // Enables/disables plugin IME.
+ virtual void SetImeEnabled(bool enabled) {};
+
+ // Synthesize a fake window handle for the plug-in to identify the instance
+ // to the browser, allowing mapping to a surface for hardware accelleration
+ // of plug-in content. The browser generates the handle which is then set on
+ // the plug-in. |opaque| indicates whether the content should be treated as
+ // opaque or translucent.
+ // TODO(stuartmorgan): Move this into WebPluginProxy.
+ virtual void BindFakePluginWindowHandle(bool opaque) {}
+
+ // Returns the accelerated surface abstraction for accelerated plugins.
+ virtual WebPluginAcceleratedSurface* GetAcceleratedSurface() { return NULL; }
+#endif
+
+ // Gets the WebPluginDelegate that implements the interface.
+ // This API is only for use with Pepper, and is only overridden
+ // by in-renderer implementations.
+ virtual WebPluginDelegate* delegate();
+
+ // Handles NPN_URLRedirectResponse calls issued by plugins in response to
+ // HTTP URL redirect notifications.
+ virtual void URLRedirectResponse(bool allow, int resource_id) = 0;
+};
+
+// Simpler version of ResourceHandleClient that lends itself to proxying.
+class WebPluginResourceClient {
+ public:
+ virtual ~WebPluginResourceClient() {}
+ virtual void WillSendRequest(const GURL& url, int http_status_code) = 0;
+ // The request_is_seekable parameter indicates whether byte range requests
+ // can be issued for the underlying stream.
+ virtual void DidReceiveResponse(const std::string& mime_type,
+ const std::string& headers,
+ uint32 expected_length,
+ uint32 last_modified,
+ bool request_is_seekable) = 0;
+ virtual void DidReceiveData(const char* buffer, int length,
+ int data_offset) = 0;
+ virtual void DidFinishLoading() = 0;
+ virtual void DidFail() = 0;
+ virtual bool IsMultiByteResponseExpected() = 0;
+ virtual int ResourceId() = 0;
+};
+
+} // namespace webkit_glue
+
+#endif // #ifndef WEBKIT_GLUE_WEBPLUGIN_H_
diff --git a/webkit/glue/plugins/webplugin_2d_device_delegate.cc b/webkit/glue/plugins/webplugin_2d_device_delegate.cc
new file mode 100644
index 0000000..f971e20
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_2d_device_delegate.cc
@@ -0,0 +1,53 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webplugin_2d_device_delegate.h"
+
+namespace webkit_glue {
+
+NPError WebPlugin2DDeviceDelegate::Device2DQueryCapability(int32 capability,
+ int32* value) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin2DDeviceDelegate::Device2DQueryConfig(
+ const NPDeviceContext2DConfig* request,
+ NPDeviceContext2DConfig* obtain) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin2DDeviceDelegate::Device2DInitializeContext(
+ const NPDeviceContext2DConfig* config,
+ NPDeviceContext2D* context) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin2DDeviceDelegate::Device2DSetStateContext(
+ NPDeviceContext2D* context,
+ int32 state,
+ intptr_t value) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin2DDeviceDelegate::Device2DGetStateContext(
+ NPDeviceContext2D* context,
+ int32 state,
+ intptr_t* value) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin2DDeviceDelegate::Device2DFlushContext(
+ NPP id,
+ NPDeviceContext2D* context,
+ NPDeviceFlushContextCallbackPtr callback,
+ void* user_data) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin2DDeviceDelegate::Device2DDestroyContext(
+ NPDeviceContext2D* context) {
+ return NPERR_GENERIC_ERROR;
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/plugins/webplugin_2d_device_delegate.h b/webkit/glue/plugins/webplugin_2d_device_delegate.h
new file mode 100644
index 0000000..e18c2fd
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_2d_device_delegate.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_WEBPLUGIN_2D_DEVICE_DELEGATE_H_
+#define WEBKIT_GLUE_PLUGINS_WEBPLUGIN_2D_DEVICE_DELEGATE_H_
+
+#include "base/basictypes.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+
+namespace webkit_glue {
+
+// Interface for the NPAPI 2D device extension. This class implements "NOP"
+// versions of all these functions so it can be used seamlessly by the
+// "regular" plugin delegate while being overridden by the "pepper" one.
+class WebPlugin2DDeviceDelegate {
+ public:
+ virtual NPError Device2DQueryCapability(int32 capability, int32* value);
+ virtual NPError Device2DQueryConfig(const NPDeviceContext2DConfig* request,
+ NPDeviceContext2DConfig* obtain);
+ virtual NPError Device2DInitializeContext(
+ const NPDeviceContext2DConfig* config,
+ NPDeviceContext2D* context);
+ virtual NPError Device2DSetStateContext(NPDeviceContext2D* context,
+ int32 state,
+ intptr_t value);
+ virtual NPError Device2DGetStateContext(NPDeviceContext2D* context,
+ int32 state,
+ intptr_t* value);
+ virtual NPError Device2DFlushContext(NPP id,
+ NPDeviceContext2D* context,
+ NPDeviceFlushContextCallbackPtr callback,
+ void* user_data);
+ virtual NPError Device2DDestroyContext(NPDeviceContext2D* context);
+
+ protected:
+ WebPlugin2DDeviceDelegate() {}
+ virtual ~WebPlugin2DDeviceDelegate() {}
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_PLUGINS_WEBPLUGIN_2D_DEVICE_DELEGATE_H_
diff --git a/webkit/glue/plugins/webplugin_3d_device_delegate.cc b/webkit/glue/plugins/webplugin_3d_device_delegate.cc
new file mode 100644
index 0000000..93dffa6
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_3d_device_delegate.cc
@@ -0,0 +1,110 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webplugin_3d_device_delegate.h"
+
+namespace webkit_glue {
+
+NPError WebPlugin3DDeviceDelegate::Device3DQueryCapability(int32 capability,
+ int32* value) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin3DDeviceDelegate::Device3DQueryConfig(
+ const NPDeviceContext3DConfig* request,
+ NPDeviceContext3DConfig* obtain) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin3DDeviceDelegate::Device3DInitializeContext(
+ const NPDeviceContext3DConfig* config,
+ NPDeviceContext3D* context) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin3DDeviceDelegate::Device3DSetStateContext(
+ NPDeviceContext3D* context,
+ int32 state,
+ intptr_t value) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin3DDeviceDelegate::Device3DGetStateContext(
+ NPDeviceContext3D* context,
+ int32 state,
+ intptr_t* value) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin3DDeviceDelegate::Device3DFlushContext(
+ NPP id,
+ NPDeviceContext3D* context,
+ NPDeviceFlushContextCallbackPtr callback,
+ void* user_data) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin3DDeviceDelegate::Device3DDestroyContext(
+ NPDeviceContext3D* context) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin3DDeviceDelegate::Device3DCreateBuffer(NPDeviceContext3D* context,
+ size_t size,
+ int32* id) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin3DDeviceDelegate::Device3DDestroyBuffer(
+ NPDeviceContext3D* context,
+ int32 id) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin3DDeviceDelegate::Device3DMapBuffer(NPDeviceContext3D* context,
+ int32 id,
+ NPDeviceBuffer* buffer) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin3DDeviceDelegate::Device3DGetNumConfigs(int32* num_configs) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin3DDeviceDelegate::Device3DGetConfigAttribs(
+ int32 config,
+ int32* attrib_list) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin3DDeviceDelegate::Device3DCreateContext(
+ int32 config,
+ const int32* attrib_list,
+ NPDeviceContext3D** context) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin3DDeviceDelegate::Device3DRegisterCallback(
+ NPP id,
+ NPDeviceContext* context,
+ int32 callback_type,
+ NPDeviceGenericCallbackPtr callback,
+ void* callback_data) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPlugin3DDeviceDelegate::Device3DSynchronizeContext(
+ NPP id,
+ NPDeviceContext3D* context,
+ NPDeviceSynchronizationMode mode,
+ const int32* input_attrib_list,
+ int32* output_attrib_list,
+ NPDeviceSynchronizeContextCallbackPtr callback,
+ void* callback_data) {
+ return NPERR_GENERIC_ERROR;
+}
+
+
+} // namespace webkit_glue
+
diff --git a/webkit/glue/plugins/webplugin_3d_device_delegate.h b/webkit/glue/plugins/webplugin_3d_device_delegate.h
new file mode 100644
index 0000000..2f64b45
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_3d_device_delegate.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_WEBPLUGIN_3D_DEVICE_DELEGATE_H_
+#define WEBKIT_GLUE_PLUGINS_WEBPLUGIN_3D_DEVICE_DELEGATE_H_
+
+#include "base/basictypes.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+
+namespace webkit_glue {
+
+// Interface for the NPAPI 3D device extension. This class implements "NOP"
+// versions of all these functions so it can be used seamlessly by the
+// "regular" plugin delegate while being overridden by the "pepper" one.
+class WebPlugin3DDeviceDelegate {
+ public:
+ virtual NPError Device3DQueryCapability(int32 capability, int32* value);
+ virtual NPError Device3DQueryConfig(const NPDeviceContext3DConfig* request,
+ NPDeviceContext3DConfig* obtain);
+ virtual NPError Device3DInitializeContext(
+ const NPDeviceContext3DConfig* config,
+ NPDeviceContext3D* context);
+ virtual NPError Device3DSetStateContext(NPDeviceContext3D* context,
+ int32 state,
+ intptr_t value);
+ virtual NPError Device3DGetStateContext(NPDeviceContext3D* context,
+ int32 state,
+ intptr_t* value);
+ virtual NPError Device3DFlushContext(NPP id,
+ NPDeviceContext3D* context,
+ NPDeviceFlushContextCallbackPtr callback,
+ void* user_data);
+ virtual NPError Device3DDestroyContext(NPDeviceContext3D* context);
+ virtual NPError Device3DCreateBuffer(NPDeviceContext3D* context,
+ size_t size,
+ int32* id);
+ virtual NPError Device3DDestroyBuffer(NPDeviceContext3D* context,
+ int32 id);
+ virtual NPError Device3DMapBuffer(NPDeviceContext3D* context,
+ int32 id,
+ NPDeviceBuffer* buffer);
+ virtual NPError Device3DGetNumConfigs(int32* num_configs);
+ virtual NPError Device3DGetConfigAttribs(int32 config,
+ int32* attrib_list);
+ virtual NPError Device3DCreateContext(int32 config,
+ const int32* attrib_list,
+ NPDeviceContext3D** context);
+ virtual NPError Device3DRegisterCallback(
+ NPP id,
+ NPDeviceContext* context,
+ int32 callback_type,
+ NPDeviceGenericCallbackPtr callback,
+ void* callback_data);
+ virtual NPError Device3DSynchronizeContext(
+ NPP id,
+ NPDeviceContext3D* context,
+ NPDeviceSynchronizationMode mode,
+ const int32* input_attrib_list,
+ int32* output_attrib_list,
+ NPDeviceSynchronizeContextCallbackPtr callback,
+ void* callback_data);
+
+ protected:
+ WebPlugin3DDeviceDelegate() {}
+ virtual ~WebPlugin3DDeviceDelegate() {}
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_PLUGINS_WEBPLUGIN_3D_DEVICE_DELEGATE_H_
diff --git a/webkit/glue/plugins/webplugin_accelerated_surface_mac.h b/webkit/glue/plugins/webplugin_accelerated_surface_mac.h
new file mode 100644
index 0000000..13980ca
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_accelerated_surface_mac.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBPLUGIN_ACCELERATED_SURFACE_MAC_H_
+#define WEBKIT_GLUE_WEBPLUGIN_ACCELERATED_SURFACE_MAC_H_
+#pragma once
+
+#include "gfx/native_widget_types.h"
+#include "gfx/size.h"
+
+// Avoid having to include OpenGL headers here.
+typedef struct _CGLContextObject* CGLContextObj;
+
+namespace webkit_glue {
+
+// Interface class for interacting with an accelerated plugin surface, used
+// for the Core Animation flavors of plugin drawing on the Mac.
+class WebPluginAcceleratedSurface {
+ public:
+ virtual ~WebPluginAcceleratedSurface() {}
+
+ // Sets the window handle used throughout the browser to identify this
+ // surface.
+ virtual void SetWindowHandle(gfx::PluginWindowHandle window) = 0;
+
+ // Sets the size of the surface.
+ virtual void SetSize(const gfx::Size& size) = 0;
+
+ // Returns the context used to draw into this surface.
+ // If initializing the surface failed, this will be NULL.
+ virtual CGLContextObj context() = 0;
+
+ // Readies the surface for drawing. Must be called before any drawing session.
+ virtual void StartDrawing() = 0;
+
+ // Ends a drawing session. Changes to the surface may not be reflected until
+ // this is called.
+ virtual void EndDrawing() = 0;
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_WEBPLUGIN_ACCELERATED_SURFACE_MAC_H_
diff --git a/webkit/glue/plugins/webplugin_audio_device_delegate.cc b/webkit/glue/plugins/webplugin_audio_device_delegate.cc
new file mode 100644
index 0000000..8cc3d62
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_audio_device_delegate.cc
@@ -0,0 +1,50 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webplugin_audio_device_delegate.h"
+
+namespace webkit_glue {
+
+NPError WebPluginAudioDeviceDelegate::DeviceAudioQueryCapability(
+ int32 capability, int32* value) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPluginAudioDeviceDelegate::DeviceAudioQueryConfig(
+ const NPDeviceContextAudioConfig* request,
+ NPDeviceContextAudioConfig* obtain) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPluginAudioDeviceDelegate::DeviceAudioInitializeContext(
+ const NPDeviceContextAudioConfig* config,
+ NPDeviceContextAudio* context) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPluginAudioDeviceDelegate::DeviceAudioSetStateContext(
+ NPDeviceContextAudio* context,
+ int32 state, intptr_t value) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPluginAudioDeviceDelegate::DeviceAudioGetStateContext(
+ NPDeviceContextAudio* context,
+ int32 state, intptr_t* value) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPluginAudioDeviceDelegate::DeviceAudioFlushContext(
+ NPP id, NPDeviceContextAudio* context,
+ NPDeviceFlushContextCallbackPtr callback, void* user_data) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError WebPluginAudioDeviceDelegate::DeviceAudioDestroyContext(
+ NPDeviceContextAudio* context) {
+ return NPERR_GENERIC_ERROR;
+}
+
+
+} // namespace webkit_glue
diff --git a/webkit/glue/plugins/webplugin_audio_device_delegate.h b/webkit/glue/plugins/webplugin_audio_device_delegate.h
new file mode 100644
index 0000000..de85433
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_audio_device_delegate.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_WEBPLUGIN_AUDIO_DEVICE_DELEGATE_H_
+#define WEBKIT_GLUE_PLUGINS_WEBPLUGIN_AUDIO_DEVICE_DELEGATE_H_
+
+#include "base/basictypes.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+
+namespace webkit_glue {
+
+// Interface for the NPAPI audio device extension. This class implements "NOP"
+// versions of all these functions so it can be used seamlessly by the
+// "regular" plugin delegate while being overridden by the "pepper" one.
+class WebPluginAudioDeviceDelegate {
+ public:
+ virtual NPError DeviceAudioQueryCapability(int32 capability, int32* value);
+ virtual NPError DeviceAudioQueryConfig(
+ const NPDeviceContextAudioConfig* request,
+ NPDeviceContextAudioConfig* obtain);
+ virtual NPError DeviceAudioInitializeContext(
+ const NPDeviceContextAudioConfig* config,
+ NPDeviceContextAudio* context);
+ virtual NPError DeviceAudioSetStateContext(NPDeviceContextAudio* context,
+ int32 state, intptr_t value);
+ virtual NPError DeviceAudioGetStateContext(NPDeviceContextAudio* context,
+ int32 state, intptr_t* value);
+ virtual NPError DeviceAudioFlushContext(
+ NPP id, NPDeviceContextAudio* context,
+ NPDeviceFlushContextCallbackPtr callback, void* user_data);
+ virtual NPError DeviceAudioDestroyContext(NPDeviceContextAudio* context);
+
+ protected:
+ WebPluginAudioDeviceDelegate() {}
+ virtual ~WebPluginAudioDeviceDelegate() {}
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_PLUGINS_WEBPLUGIN_AUDIO_DEVICE_DELEGATE_H_
+
diff --git a/webkit/glue/plugins/webplugin_delegate.cc b/webkit/glue/plugins/webplugin_delegate.cc
new file mode 100644
index 0000000..c3fb53b
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_delegate.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webplugin_delegate.h"
+
+namespace webkit_glue {
+
+bool WebPluginDelegate::StartFind(const string16& search_text,
+ bool case_sensitive,
+ int identifier) {
+ return false;
+}
+
+NPWidgetExtensions* WebPluginDelegate::GetWidgetExtensions() {
+ return NULL;
+}
+
+bool WebPluginDelegate::SetCursor(NPCursorType type) {
+ return false;
+}
+
+NPFontExtensions* WebPluginDelegate::GetFontExtensions() {
+ return NULL;
+}
+
+bool WebPluginDelegate::HasSelection() const {
+ return false;
+}
+
+string16 WebPluginDelegate::GetSelectionAsText() const {
+ return string16();
+}
+
+string16 WebPluginDelegate::GetSelectionAsMarkup() const {
+ return string16();
+}
+
+
+} // namespace webkit_glue
diff --git a/webkit/glue/plugins/webplugin_delegate.h b/webkit/glue/plugins/webplugin_delegate.h
new file mode 100644
index 0000000..ac7bb5c
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_delegate.h
@@ -0,0 +1,166 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBPLUGIN_DELEGATE_H_
+#define WEBKIT_GLUE_WEBPLUGIN_DELEGATE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/string16.h"
+#include "build/build_config.h"
+#include "gfx/native_widget_types.h"
+#include "third_party/npapi/bindings/npapi.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCanvas.h"
+#include "webkit/glue/plugins/webplugin_2d_device_delegate.h"
+#include "webkit/glue/plugins/webplugin_3d_device_delegate.h"
+#include "webkit/glue/plugins/webplugin_audio_device_delegate.h"
+#include "webkit/glue/plugins/webplugin_file_delegate.h"
+#include "webkit/glue/plugins/webplugin_print_delegate.h"
+
+class FilePath;
+class GURL;
+struct NPObject;
+
+namespace WebKit {
+class WebInputEvent;
+struct WebCursorInfo;
+}
+
+namespace gfx {
+class Rect;
+}
+
+namespace webkit_glue {
+
+class WebPlugin;
+class WebPluginResourceClient;
+
+// This is the interface that a plugin implementation needs to provide.
+class WebPluginDelegate : public WebPlugin2DDeviceDelegate,
+ public WebPlugin3DDeviceDelegate,
+ public WebPluginAudioDeviceDelegate,
+ public WebPluginPrintDelegate,
+ public WebPluginFileDelegate {
+ public:
+ virtual ~WebPluginDelegate() {}
+
+ // Initializes the plugin implementation with the given (UTF8) arguments.
+ // Note that the lifetime of WebPlugin must be longer than this delegate.
+ // If this function returns false the plugin isn't started and shouldn't be
+ // called again. If this method succeeds, then the WebPlugin is valid until
+ // PluginDestroyed is called.
+ // The load_manually parameter if true indicates that the plugin data would
+ // be passed from webkit. if false indicates that the plugin should download
+ // the data. This also controls whether the plugin is instantiated as a full
+ // page plugin (NP_FULL) or embedded (NP_EMBED).
+ virtual bool Initialize(const GURL& url,
+ const std::vector<std::string>& arg_names,
+ const std::vector<std::string>& arg_values,
+ WebPlugin* plugin,
+ bool load_manually) = 0;
+
+ // Called when the WebPlugin is being destroyed. This is a signal to the
+ // delegate that it should tear-down the plugin implementation and not call
+ // methods on the WebPlugin again.
+ virtual void PluginDestroyed() = 0;
+
+ // Update the geometry of the plugin. This is a request to move the
+ // plugin, relative to its containing window, to the coords given by
+ // window_rect. Its contents should be clipped to the coords given
+ // by clip_rect, which are relative to the origin of the plugin
+ // window. The clip_rect is in plugin-relative coordinates.
+ virtual void UpdateGeometry(const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect) = 0;
+
+ // Tells the plugin to paint the damaged rect. |canvas| is only used for
+ // windowless plugins.
+ virtual void Paint(WebKit::WebCanvas* canvas, const gfx::Rect& rect) = 0;
+
+ // Tells the plugin to print itself.
+ virtual void Print(gfx::NativeDrawingContext hdc) = 0;
+
+ // Informs the plugin that it has gained or lost focus. This is only called in
+ // windowless mode.
+ virtual void SetFocus(bool focused) = 0;
+
+ // For windowless plugins, gives them a user event like mouse/keyboard.
+ // Returns whether the event was handled. This is only called in windowsless
+ // mode. See NPAPI NPP_HandleEvent for more information.
+ virtual bool HandleInputEvent(const WebKit::WebInputEvent& event,
+ WebKit::WebCursorInfo* cursor) = 0;
+
+ // Gets the NPObject associated with the plugin for scripting.
+ virtual NPObject* GetPluginScriptableObject() = 0;
+
+ // Receives notification about a resource load that the plugin initiated
+ // for a frame.
+ virtual void DidFinishLoadWithReason(const GURL& url, NPReason reason,
+ int notify_id) = 0;
+
+ // Returns the process id of the process that is running the plugin.
+ virtual int GetProcessId() = 0;
+
+ // The result, UTF-8 encoded, of the script execution is returned via this
+ // function.
+ virtual void SendJavaScriptStream(const GURL& url,
+ const std::string& result,
+ bool success,
+ int notify_id) = 0;
+
+ // Receives notification about data being available.
+ virtual void DidReceiveManualResponse(const GURL& url,
+ const std::string& mime_type,
+ const std::string& headers,
+ uint32 expected_length,
+ uint32 last_modified) = 0;
+
+ // Receives the data.
+ virtual void DidReceiveManualData(const char* buffer, int length) = 0;
+
+ // Indicates end of data load.
+ virtual void DidFinishManualLoading() = 0;
+
+ // Indicates a failure in data receipt.
+ virtual void DidManualLoadFail() = 0;
+
+ // Only supported when the plugin is the default plugin.
+ virtual void InstallMissingPlugin() = 0;
+
+ // Creates a WebPluginResourceClient instance and returns the same.
+ virtual WebPluginResourceClient* CreateResourceClient(
+ unsigned long resource_id,
+ const GURL& url,
+ int notify_id) = 0;
+
+ // Creates a WebPluginResourceClient instance for an existing stream that is
+ // has become seekable.
+ virtual WebPluginResourceClient* CreateSeekableResourceClient(
+ unsigned long resource_id, int range_request_id) = 0;
+
+ // See WebPluginContainerImpl's description of the interface.
+ virtual bool StartFind(const string16& search_text,
+ bool case_sensitive,
+ int identifier);
+ virtual void SelectFindResult(bool forward) {}
+ virtual void StopFind() {}
+ virtual void NumberOfFindResultsChanged(int total, bool final_result) {}
+ virtual void SelectedFindResultChanged(int index) {}
+ virtual NPWidgetExtensions* GetWidgetExtensions();
+ virtual bool SetCursor(NPCursorType type);
+ virtual NPFontExtensions* GetFontExtensions();
+
+ // Used for zooming of full page plugins. 0 means reset, while -1 means zoom
+ // out and +1 means zoom in.
+ virtual void SetZoomFactor(float scale, bool text_only) {}
+ // Gets the selected text, if any.
+ virtual bool HasSelection() const;
+ virtual string16 GetSelectionAsText() const;
+ virtual string16 GetSelectionAsMarkup() const;
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_WEBPLUGIN_DELEGATE_H_
diff --git a/webkit/glue/plugins/webplugin_delegate_impl.cc b/webkit/glue/plugins/webplugin_delegate_impl.cc
new file mode 100644
index 0000000..e3e4f9d
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_delegate_impl.cc
@@ -0,0 +1,304 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webplugin_delegate_impl.h"
+
+#include <string>
+#include <vector>
+
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
+#include "webkit/glue/plugins/plugin_constants_win.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/glue/plugins/plugin_stream_url.h"
+#include "webkit/glue/webkit_glue.h"
+
+using webkit_glue::WebPlugin;
+using webkit_glue::WebPluginDelegate;
+using webkit_glue::WebPluginResourceClient;
+using WebKit::WebCursorInfo;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebInputEvent;
+using WebKit::WebMouseEvent;
+
+WebPluginDelegateImpl* WebPluginDelegateImpl::Create(
+ const FilePath& filename,
+ const std::string& mime_type,
+ gfx::PluginWindowHandle containing_view) {
+ scoped_refptr<NPAPI::PluginLib> plugin_lib(
+ NPAPI::PluginLib::CreatePluginLib(filename));
+ if (plugin_lib.get() == NULL)
+ return NULL;
+
+ NPError err = plugin_lib->NP_Initialize();
+ if (err != NPERR_NO_ERROR)
+ return NULL;
+
+ scoped_refptr<NPAPI::PluginInstance> instance(
+ plugin_lib->CreateInstance(mime_type));
+ return new WebPluginDelegateImpl(containing_view, instance.get());
+}
+
+void WebPluginDelegateImpl::PluginDestroyed() {
+ if (handle_event_depth_) {
+ MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+ } else {
+ delete this;
+ }
+}
+
+bool WebPluginDelegateImpl::Initialize(
+ const GURL& url,
+ const std::vector<std::string>& arg_names,
+ const std::vector<std::string>& arg_values,
+ WebPlugin* plugin,
+ bool load_manually) {
+ plugin_ = plugin;
+
+ instance_->set_web_plugin(plugin_);
+ if (quirks_ & PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES) {
+ NPAPI::PluginLib* plugin_lib = instance()->plugin_lib();
+ if (plugin_lib->instance_count() > 1) {
+ return false;
+ }
+ }
+
+ if (quirks_ & PLUGIN_QUIRK_DIE_AFTER_UNLOAD)
+ webkit_glue::SetForcefullyTerminatePluginProcess(true);
+
+ int argc = 0;
+ scoped_array<char*> argn(new char*[arg_names.size()]);
+ scoped_array<char*> argv(new char*[arg_names.size()]);
+ for (size_t i = 0; i < arg_names.size(); ++i) {
+ if (quirks_ & PLUGIN_QUIRK_NO_WINDOWLESS &&
+ LowerCaseEqualsASCII(arg_names[i], "windowlessvideo")) {
+ continue;
+ }
+ argn[argc] = const_cast<char*>(arg_names[i].c_str());
+ argv[argc] = const_cast<char*>(arg_values[i].c_str());
+ argc++;
+ }
+
+ creation_succeeded_ = instance_->Start(
+ url, argn.get(), argv.get(), argc, load_manually);
+ if (!creation_succeeded_)
+ return false;
+
+ windowless_ = instance_->windowless();
+ if (!windowless_) {
+ if (!WindowedCreatePlugin())
+ return false;
+ } else {
+ // For windowless plugins we should set the containing window handle
+ // as the instance window handle. This is what Safari does. Not having
+ // a valid window handle causes subtle bugs with plugins which retrieve
+ // the window handle and validate the same. The window handle can be
+ // retrieved via NPN_GetValue of NPNVnetscapeWindow.
+ instance_->set_window_handle(parent_);
+ }
+
+ bool should_load = PlatformInitialize();
+
+ plugin_url_ = url.spec();
+
+ return should_load;
+}
+
+void WebPluginDelegateImpl::DestroyInstance() {
+ if (instance_ && (instance_->npp()->ndata != NULL)) {
+ // Shutdown all streams before destroying so that
+ // no streams are left "in progress". Need to do
+ // this before calling set_web_plugin(NULL) because the
+ // instance uses the helper to do the download.
+ instance_->CloseStreams();
+
+ window_.window = NULL;
+ if (creation_succeeded_ &&
+ !(quirks_ & PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY)) {
+ instance_->NPP_SetWindow(&window_);
+ }
+
+ instance_->NPP_Destroy();
+
+ instance_->set_web_plugin(NULL);
+
+ PlatformDestroyInstance();
+
+ instance_ = 0;
+ }
+}
+
+void WebPluginDelegateImpl::UpdateGeometry(
+ const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect) {
+
+ if (first_set_window_call_) {
+ first_set_window_call_ = false;
+ // Plugins like media player on Windows have a bug where in they handle the
+ // first geometry update and ignore the rest resulting in painting issues.
+ // This quirk basically ignores the first set window call sequence for
+ // these plugins and has been tested for Windows plugins only.
+ if (quirks_ & PLUGIN_QUIRK_IGNORE_FIRST_SETWINDOW_CALL)
+ return;
+ }
+
+ if (windowless_) {
+ WindowlessUpdateGeometry(window_rect, clip_rect);
+ } else {
+ WindowedUpdateGeometry(window_rect, clip_rect);
+ }
+}
+
+void WebPluginDelegateImpl::SetFocus(bool focused) {
+ DCHECK(windowless_);
+ // This is called when internal WebKit focus (the focused element on the page)
+ // changes, but plugins need to know about OS-level focus, so we have an extra
+ // layer of focus tracking.
+ //
+ // On Windows, historically browsers did not set focus events to windowless
+ // plugins when the toplevel window focus changes. Sending such focus events
+ // breaks full screen mode in Flash because it will come out of full screen
+ // mode when it loses focus, and its full screen window causes the browser to
+ // lose focus.
+ has_webkit_focus_ = focused;
+#ifndef OS_WIN
+ if (containing_view_has_focus_)
+ SetPluginHasFocus(focused);
+#else
+ SetPluginHasFocus(focused);
+#endif
+}
+
+void WebPluginDelegateImpl::SetPluginHasFocus(bool focused) {
+ if (focused == plugin_has_focus_)
+ return;
+ if (PlatformSetPluginHasFocus(focused))
+ plugin_has_focus_ = focused;
+}
+
+void WebPluginDelegateImpl::SetContentAreaHasFocus(bool has_focus) {
+ containing_view_has_focus_ = has_focus;
+ if (!windowless_)
+ return;
+#ifndef OS_WIN // See SetFocus above.
+ SetPluginHasFocus(containing_view_has_focus_ && has_webkit_focus_);
+#endif
+}
+
+NPObject* WebPluginDelegateImpl::GetPluginScriptableObject() {
+ return instance_->GetPluginScriptableObject();
+}
+
+void WebPluginDelegateImpl::DidFinishLoadWithReason(const GURL& url,
+ NPReason reason,
+ int notify_id) {
+ if (quirks_ & PLUGIN_QUIRK_ALWAYS_NOTIFY_SUCCESS &&
+ reason == NPRES_NETWORK_ERR) {
+ // Flash needs this or otherwise it unloads the launching swf object.
+ reason = NPRES_DONE;
+ }
+
+ instance()->DidFinishLoadWithReason(url, reason, notify_id);
+}
+
+int WebPluginDelegateImpl::GetProcessId() {
+ // We are in process, so the plugin pid is this current process pid.
+ return base::GetCurrentProcId();
+}
+
+void WebPluginDelegateImpl::SendJavaScriptStream(const GURL& url,
+ const std::string& result,
+ bool success,
+ int notify_id) {
+ instance()->SendJavaScriptStream(url, result, success, notify_id);
+}
+
+void WebPluginDelegateImpl::DidReceiveManualResponse(
+ const GURL& url, const std::string& mime_type,
+ const std::string& headers, uint32 expected_length, uint32 last_modified) {
+ if (!windowless_) {
+ // Calling NPP_WriteReady before NPP_SetWindow causes movies to not load in
+ // Flash. See http://b/issue?id=892174.
+ DCHECK(windowed_did_set_window_);
+ }
+
+ instance()->DidReceiveManualResponse(url, mime_type, headers,
+ expected_length, last_modified);
+}
+
+void WebPluginDelegateImpl::DidReceiveManualData(const char* buffer,
+ int length) {
+ instance()->DidReceiveManualData(buffer, length);
+}
+
+void WebPluginDelegateImpl::DidFinishManualLoading() {
+ instance()->DidFinishManualLoading();
+}
+
+void WebPluginDelegateImpl::DidManualLoadFail() {
+ instance()->DidManualLoadFail();
+}
+
+FilePath WebPluginDelegateImpl::GetPluginPath() {
+ return instance()->plugin_lib()->plugin_info().path;
+}
+
+void WebPluginDelegateImpl::WindowedUpdateGeometry(
+ const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect) {
+ if (WindowedReposition(window_rect, clip_rect) ||
+ !windowed_did_set_window_) {
+ // Let the plugin know that it has been moved
+ WindowedSetWindow();
+ }
+}
+
+bool WebPluginDelegateImpl::HandleInputEvent(const WebInputEvent& event,
+ WebCursorInfo* cursor_info) {
+ DCHECK(windowless_) << "events should only be received in windowless mode";
+
+ bool pop_user_gesture = false;
+ if (IsUserGesture(event)) {
+ pop_user_gesture = true;
+ instance()->PushPopupsEnabledState(true);
+ }
+
+ bool handled = PlatformHandleInputEvent(event, cursor_info);
+
+ if (pop_user_gesture) {
+ instance()->PopPopupsEnabledState();
+ }
+
+ return handled;
+}
+
+bool WebPluginDelegateImpl::IsUserGesture(const WebInputEvent& event) {
+ switch (event.type) {
+ case WebInputEvent::MouseDown:
+ case WebInputEvent::MouseUp:
+ case WebInputEvent::KeyDown:
+ case WebInputEvent::KeyUp:
+ return true;
+ default:
+ return false;
+ }
+ return false;
+}
+
+WebPluginResourceClient* WebPluginDelegateImpl::CreateResourceClient(
+ unsigned long resource_id, const GURL& url, int notify_id) {
+ return instance()->CreateStream(
+ resource_id, url, std::string(), notify_id);
+}
+
+WebPluginResourceClient* WebPluginDelegateImpl::CreateSeekableResourceClient(
+ unsigned long resource_id, int range_request_id) {
+ return instance()->GetRangeRequest(range_request_id);
+}
diff --git a/webkit/glue/plugins/webplugin_delegate_impl.h b/webkit/glue/plugins/webplugin_delegate_impl.h
new file mode 100644
index 0000000..4046c95
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_delegate_impl.h
@@ -0,0 +1,511 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_WEBPLUGIN_DELEGATE_IMPL_H_
+#define WEBKIT_GLUE_PLUGINS_WEBPLUGIN_DELEGATE_IMPL_H_
+
+#include "build/build_config.h"
+
+#include <string>
+#include <list>
+
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/task.h"
+#include "base/time.h"
+#include "base/timer.h"
+#include "gfx/native_widget_types.h"
+#include "gfx/rect.h"
+#include "third_party/npapi/bindings/npapi.h"
+#include "webkit/glue/plugins/webplugin_delegate.h"
+#include "webkit/glue/webcursor.h"
+
+#if defined(USE_X11)
+#include "app/x11_util.h"
+
+typedef struct _GdkDrawable GdkPixmap;
+#endif
+
+class FilePath;
+
+namespace NPAPI {
+class PluginInstance;
+}
+
+namespace WebKit {
+class WebMouseEvent;
+}
+
+#if defined(OS_MACOSX)
+class ExternalDragTracker;
+#ifndef NP_NO_QUICKDRAW
+class QuickDrawDrawingManager;
+#endif
+#ifdef __OBJC__
+@class CALayer;
+@class CARenderer;
+#else
+class CALayer;
+class CARenderer;
+#endif
+namespace webkit_glue {
+class WebPluginAcceleratedSurface;
+}
+#endif
+
+// An implementation of WebPluginDelegate that runs in the plugin process,
+// proxied from the renderer by WebPluginDelegateProxy.
+class WebPluginDelegateImpl : public webkit_glue::WebPluginDelegate {
+ public:
+ enum PluginQuirks {
+ PLUGIN_QUIRK_SETWINDOW_TWICE = 1, // Win32
+ PLUGIN_QUIRK_THROTTLE_WM_USER_PLUS_ONE = 2, // Win32
+ PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY = 4, // Win32
+ PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY = 8, // Win32
+ PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES = 16, // Win32
+ PLUGIN_QUIRK_DIE_AFTER_UNLOAD = 32, // Win32
+ PLUGIN_QUIRK_PATCH_SETCURSOR = 64, // Win32
+ PLUGIN_QUIRK_BLOCK_NONSTANDARD_GETURL_REQUESTS = 128, // Win32
+ PLUGIN_QUIRK_WINDOWLESS_OFFSET_WINDOW_TO_DRAW = 256, // Linux
+ PLUGIN_QUIRK_WINDOWLESS_INVALIDATE_AFTER_SET_WINDOW = 512, // Linux
+ PLUGIN_QUIRK_NO_WINDOWLESS = 1024, // Windows
+ PLUGIN_QUIRK_PATCH_REGENUMKEYEXW = 2048, // Windows
+ PLUGIN_QUIRK_ALWAYS_NOTIFY_SUCCESS = 4096, // Windows
+ PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH = 8192, // Mac
+ PLUGIN_QUIRK_HANDLE_MOUSE_CAPTURE = 16384, // Windows
+ PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK = 32768, // Linux
+ PLUGIN_QUIRK_IGNORE_FIRST_SETWINDOW_CALL = 65536, // Windows.
+ };
+
+ static WebPluginDelegateImpl* Create(const FilePath& filename,
+ const std::string& mime_type,
+ gfx::PluginWindowHandle containing_view);
+
+ static bool IsPluginDelegateWindow(gfx::NativeWindow window);
+ static bool GetPluginNameFromWindow(gfx::NativeWindow window,
+ std::wstring *plugin_name);
+
+ // Returns true if the window handle passed in is that of the dummy
+ // activation window for windowless plugins.
+ static bool IsDummyActivationWindow(gfx::NativeWindow window);
+
+ // WebPluginDelegate implementation
+ virtual bool Initialize(const GURL& url,
+ const std::vector<std::string>& arg_names,
+ const std::vector<std::string>& arg_values,
+ webkit_glue::WebPlugin* plugin,
+ bool load_manually);
+ virtual void PluginDestroyed();
+ virtual void UpdateGeometry(const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect);
+ virtual void Paint(WebKit::WebCanvas* canvas, const gfx::Rect& rect);
+ virtual void Print(gfx::NativeDrawingContext context);
+ virtual void SetFocus(bool focused);
+ virtual bool HandleInputEvent(const WebKit::WebInputEvent& event,
+ WebKit::WebCursorInfo* cursor_info);
+ virtual NPObject* GetPluginScriptableObject();
+ virtual void DidFinishLoadWithReason(
+ const GURL& url, NPReason reason, int notify_id);
+ virtual int GetProcessId();
+ virtual void SendJavaScriptStream(const GURL& url,
+ const std::string& result,
+ bool success,
+ int notify_id);
+ virtual void DidReceiveManualResponse(const GURL& 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 void InstallMissingPlugin();
+ virtual webkit_glue::WebPluginResourceClient* CreateResourceClient(
+ unsigned long resource_id, const GURL& url, int notify_id);
+ virtual webkit_glue::WebPluginResourceClient* CreateSeekableResourceClient(
+ unsigned long resource_id, int range_request_id);
+ // End of WebPluginDelegate implementation.
+
+ bool IsWindowless() const { return windowless_ ; }
+ gfx::Rect GetRect() const { return window_rect_; }
+ gfx::Rect GetClipRect() const { return clip_rect_; }
+
+ // Returns the path for the library implementing this plugin.
+ FilePath GetPluginPath();
+
+ // Returns a combination of PluginQuirks.
+ int GetQuirks() const { return quirks_; }
+
+ // Informs the plugin that the view it is in has gained or lost focus.
+ void SetContentAreaHasFocus(bool has_focus);
+
+#if defined(OS_MACOSX)
+ // Informs the plugin that the geometry has changed, as with UpdateGeometry,
+ // but also includes the new buffer context for that new geometry.
+ void UpdateGeometryAndContext(const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect,
+ gfx::NativeDrawingContext context);
+ // Informs the delegate that the plugin called NPN_Invalidate*. Used as a
+ // trigger for Core Animation drawing.
+ void PluginDidInvalidate();
+ // Returns the delegate currently processing events.
+ static WebPluginDelegateImpl* GetActiveDelegate();
+ // Informs the plugin that the window it is in has gained or lost focus.
+ void SetWindowHasFocus(bool has_focus);
+ // Returns whether or not the window the plugin is in has focus.
+ bool GetWindowHasFocus() const { return containing_window_has_focus_; }
+ // Informs the plugin that its tab or window has been hidden or shown.
+ void SetContainerVisibility(bool is_visible);
+ // Informs the plugin that its containing window's frame has changed.
+ // Frames are in screen coordinates.
+ void WindowFrameChanged(const gfx::Rect& window_frame,
+ const gfx::Rect& view_frame);
+ // Informs the plugin that IME composition has been confirmed.
+ void ImeCompositionConfirmed(const string16& text);
+ // Informs the delegate that the plugin set a Carbon ThemeCursor.
+ void SetThemeCursor(ThemeCursor cursor);
+ // Informs the delegate that the plugin set a Carbon Cursor.
+ void SetCursor(const Cursor* cursor);
+ // Informs the delegate that the plugin set a Cocoa NSCursor.
+ void SetNSCursor(NSCursor* cursor);
+
+#ifndef NP_NO_CARBON
+ // Indicates that it's time to send the plugin a null event.
+ void FireIdleEvent();
+#endif
+#endif // OS_MACOSX
+
+ gfx::PluginWindowHandle windowed_handle() const {
+ return windowed_handle_;
+ }
+
+#if defined(OS_MACOSX)
+ // Allow setting a "fake" window handle to associate this plug-in with
+ // an IOSurface in the browser. Used for accelerated drawing surfaces.
+ void set_windowed_handle(gfx::PluginWindowHandle handle);
+#endif
+
+#if defined(USE_X11)
+ void SetWindowlessShmPixmap(XID shm_pixmap) {
+ windowless_shm_pixmap_ = shm_pixmap;
+ }
+#endif
+
+ private:
+ friend class DeleteTask<WebPluginDelegateImpl>;
+ friend class webkit_glue::WebPluginDelegate;
+
+ WebPluginDelegateImpl(gfx::PluginWindowHandle containing_view,
+ NPAPI::PluginInstance *instance);
+ ~WebPluginDelegateImpl();
+
+ // Called by Initialize() for platform-specific initialization.
+ // If this returns false, the plugin shouldn't be started--see Initialize().
+ bool PlatformInitialize();
+
+ // Called by DestroyInstance(), used for platform-specific destruction.
+ void PlatformDestroyInstance();
+
+ //--------------------------
+ // used for windowed plugins
+ void WindowedUpdateGeometry(const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect);
+ // Create the native window.
+ // Returns true if the window is created (or already exists).
+ // Returns false if unable to create the window.
+ bool WindowedCreatePlugin();
+
+ // Destroy the native window.
+ void WindowedDestroyWindow();
+
+ // Reposition the native window to be in sync with the given geometry.
+ // Returns true if the native window has moved or been clipped differently.
+ bool WindowedReposition(const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect);
+
+ // Tells the plugin about the current state of the window.
+ // See NPAPI NPP_SetWindow for more information.
+ void WindowedSetWindow();
+
+#if defined(OS_WIN)
+ // Registers the window class for our window
+ ATOM RegisterNativeWindowClass();
+
+ // Our WndProc functions.
+ static LRESULT CALLBACK DummyWindowProc(
+ HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+ static LRESULT CALLBACK NativeWndProc(
+ HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
+ static LRESULT CALLBACK FlashWindowlessWndProc(
+ HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
+
+ // Used for throttling Flash messages.
+ static void ClearThrottleQueueForWindow(HWND window);
+ static void OnThrottleMessage();
+ static void ThrottleMessage(WNDPROC proc, HWND hwnd, UINT message,
+ WPARAM wParam, LPARAM lParam);
+#endif
+
+ //----------------------------
+ // used for windowless plugins
+ void WindowlessUpdateGeometry(const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect);
+ void WindowlessPaint(gfx::NativeDrawingContext hdc, const gfx::Rect& rect);
+
+ // Tells the plugin about the current state of the window.
+ // See NPAPI NPP_SetWindow for more information.
+ void WindowlessSetWindow();
+
+ // Informs the plugin that it has gained or lost keyboard focus (on the Mac,
+ // this just means window first responder status).
+ void SetPluginHasFocus(bool focused);
+
+ // Handles the platform specific details of setting plugin focus. Returns
+ // false if the platform cancelled the focus tranfer.
+ bool PlatformSetPluginHasFocus(bool focused);
+
+ //-----------------------------------------
+ // used for windowed and windowless plugins
+
+ // Does platform-specific event handling. Arguments and return are identical
+ // to HandleInputEvent.
+ bool PlatformHandleInputEvent(const WebKit::WebInputEvent& event,
+ WebKit::WebCursorInfo* cursor_info);
+
+ NPAPI::PluginInstance* instance() { return instance_.get(); }
+
+ // Closes down and destroys our plugin instance.
+ void DestroyInstance();
+
+
+ // used for windowed plugins
+ // Note: on Mac OS X, the only time the windowed handle is non-zero
+ // is the case of accelerated rendering, which uses a fake window handle to
+ // identify itself back to the browser. It still performs all of its
+ // work offscreen.
+ gfx::PluginWindowHandle windowed_handle_;
+ gfx::Rect windowed_last_pos_;
+
+ bool windowed_did_set_window_;
+
+ // used by windowed and windowless plugins
+ bool windowless_;
+
+ webkit_glue::WebPlugin* plugin_;
+ scoped_refptr<NPAPI::PluginInstance> instance_;
+
+#if defined(OS_WIN)
+ // Original wndproc before we subclassed.
+ WNDPROC plugin_wnd_proc_;
+
+ // Used to throttle WM_USER+1 messages in Flash.
+ uint32 last_message_;
+ bool is_calling_wndproc;
+
+ // The current keyboard layout of this process and the main thread ID of the
+ // browser process. These variables are used for synchronizing the keyboard
+ // layout of this process with the one of the browser process.
+ HKL keyboard_layout_;
+ int parent_thread_id_;
+#endif // defined(OS_WIN)
+
+#if defined(USE_X11)
+ // The SHM pixmap for a windowless plugin.
+ XID windowless_shm_pixmap_;
+
+ // The pixmap we're drawing into, for a windowless plugin.
+ GdkPixmap* pixmap_;
+ double first_event_time_;
+
+ // On Linux some plugins assume that the GtkSocket container is in the same
+ // process. So we create a GtkPlug to plug into the browser's container, and
+ // a GtkSocket to hold the plugin. We then send the GtkPlug to the browser
+ // process.
+ GtkWidget* plug_;
+ GtkWidget* socket_;
+
+ // Ensure pixmap_ exists and is at least width by height pixels.
+ void EnsurePixmapAtLeastSize(int width, int height);
+#endif
+
+ gfx::PluginWindowHandle parent_;
+ NPWindow window_;
+ gfx::Rect window_rect_;
+ gfx::Rect clip_rect_;
+ int quirks_;
+
+#if defined(OS_WIN)
+ // Windowless plugins don't have keyboard focus causing issues with the
+ // plugin not receiving keyboard events if the plugin enters a modal
+ // loop like TrackPopupMenuEx or MessageBox, etc.
+ // This is a basic issue with windows activation and focus arising due to
+ // the fact that these windows are created by different threads. Activation
+ // and focus are thread specific states, and if the browser has focus,
+ // the plugin may not have focus.
+ // To fix a majority of these activation issues we create a dummy visible
+ // child window to which we set focus whenever the windowless plugin
+ // receives a WM_LBUTTONDOWN/WM_RBUTTONDOWN message via NPP_HandleEvent.
+
+ HWND dummy_window_for_activation_;
+ bool CreateDummyWindowForActivation();
+
+ // Returns true if the event passed in needs to be tracked for a potential
+ // modal loop.
+ static bool ShouldTrackEventForModalLoops(NPEvent* event);
+
+ // The message filter hook procedure, which tracks modal loops entered by
+ // a plugin in the course of a NPP_HandleEvent call.
+ static LRESULT CALLBACK HandleEventMessageFilterHook(int code, WPARAM wParam,
+ LPARAM lParam);
+
+ // TrackPopupMenu interceptor. Parameters are the same as the Win32 function
+ // TrackPopupMenu.
+ static BOOL WINAPI TrackPopupMenuPatch(HMENU menu, unsigned int flags, int x,
+ int y, int reserved, HWND window,
+ const RECT* rect);
+
+ // SetCursor interceptor for windowless plugins.
+ static HCURSOR WINAPI SetCursorPatch(HCURSOR cursor);
+
+ // RegEnumKeyExW interceptor.
+ static LONG WINAPI RegEnumKeyExWPatch(
+ HKEY key, DWORD index, LPWSTR name, LPDWORD name_size, LPDWORD reserved,
+ LPWSTR class_name, LPDWORD class_size, PFILETIME last_write_time);
+
+ // The mouse hook proc which handles mouse capture in windowed plugins.
+ static LRESULT CALLBACK MouseHookProc(int code, WPARAM wParam,
+ LPARAM lParam);
+
+ // Calls SetCapture/ReleaseCapture based on the message type.
+ static void HandleCaptureForMessage(HWND window, UINT message);
+
+#elif defined(OS_MACOSX)
+ // Sets window_rect_ to |rect|
+ void SetPluginRect(const gfx::Rect& rect);
+ // Sets content_area_origin to |origin|
+ void SetContentAreaOrigin(const gfx::Point& origin);
+ // Updates everything that depends on the plugin's absolute screen location.
+ void PluginScreenLocationChanged();
+ // Updates anything that depends on plugin visibility.
+ void PluginVisibilityChanged();
+
+ // Enables/disables IME.
+ void SetImeEnabled(bool enabled);
+
+ // Informs the browser about the updated accelerated drawing surface.
+ void UpdateAcceleratedSurface();
+
+ // Uses a CARenderer to draw the plug-in's layer in our OpenGL surface.
+ void DrawLayerInSurface();
+
+#ifndef NP_NO_CARBON
+ // Moves our dummy window to match the current screen location of the plugin.
+ void UpdateDummyWindowBounds(const gfx::Point& plugin_origin);
+
+#ifndef NP_NO_QUICKDRAW
+ // Sets the mode used for QuickDraw plugin drawing. If enabled is true the
+ // plugin draws into a GWorld that's not connected to a window (the faster
+ // path), otherwise the plugin draws into our invisible dummy window (which is
+ // slower, since the call we use to scrape the window contents is much more
+ // expensive than copying between GWorlds).
+ void SetQuickDrawFastPathEnabled(bool enabled);
+#endif
+
+ // Adjusts the idle event rate for a Carbon plugin based on its current
+ // visibility.
+ void UpdateIdleEventRate();
+#endif // !NP_NO_CARBON
+
+ CGContextRef buffer_context_; // Weak ref.
+
+#ifndef NP_NO_CARBON
+ NP_CGContext np_cg_context_;
+#endif
+#ifndef NP_NO_QUICKDRAW
+ NP_Port qd_port_;
+ scoped_ptr<QuickDrawDrawingManager> qd_manager_;
+ base::TimeTicks fast_path_enable_tick_;
+#endif
+
+ CALayer* layer_; // Used for CA drawing mode. Weak, retained by plug-in.
+ webkit_glue::WebPluginAcceleratedSurface* surface_; // Weak ref.
+ CARenderer* renderer_; // Renders layer_ to surface_.
+ scoped_ptr<base::RepeatingTimer<WebPluginDelegateImpl> > redraw_timer_;
+
+ // The upper-left corner of the web content area in screen coordinates,
+ // relative to an upper-left (0,0).
+ gfx::Point content_area_origin_;
+
+ bool containing_window_has_focus_;
+ bool initial_window_focus_;
+ bool container_is_visible_;
+ bool have_called_set_window_;
+
+ gfx::Rect cached_clip_rect_;
+
+ bool ime_enabled_;
+
+ scoped_ptr<ExternalDragTracker> external_drag_tracker_;
+#endif // OS_MACOSX
+
+ // Called by the message filter hook when the plugin enters a modal loop.
+ void OnModalLoopEntered();
+
+ // Returns true if the message passed in corresponds to a user gesture.
+ static bool IsUserGesture(const WebKit::WebInputEvent& event);
+
+ // The url with which the plugin was instantiated.
+ std::string plugin_url_;
+
+#if defined(OS_WIN)
+ // Indicates the end of a user gesture period.
+ void OnUserGestureEnd();
+
+ // Handle to the message filter hook
+ HHOOK handle_event_message_filter_hook_;
+
+ // Event which is set when the plugin enters a modal loop in the course
+ // of a NPP_HandleEvent call.
+ HANDLE handle_event_pump_messages_event_;
+
+ // This flag indicates whether we started tracking a user gesture message.
+ bool user_gesture_message_posted_;
+
+ // Runnable Method Factory used to invoke the OnUserGestureEnd method
+ // asynchronously.
+ ScopedRunnableMethodFactory<WebPluginDelegateImpl> user_gesture_msg_factory_;
+
+ // Handle to the mouse hook installed for certain windowed plugins like
+ // flash.
+ HHOOK mouse_hook_;
+#endif
+
+ // Holds the depth of the HandleEvent callstack.
+ int handle_event_depth_;
+
+ // Holds the current cursor set by the windowless plugin.
+ WebCursor current_windowless_cursor_;
+
+ // Set to true initially and indicates if this is the first npp_setwindow
+ // call received by the plugin.
+ bool first_set_window_call_;
+
+ // True if the plugin thinks it has keyboard focus
+ bool plugin_has_focus_;
+ // True if the plugin element has focus within the web content, regardless of
+ // whether its containing view currently has focus.
+ bool has_webkit_focus_;
+ // True if the containing view currently has focus.
+ // Initially set to true so that plugin focus still works in environments
+ // where SetContentAreaHasFocus is never called. See
+ // https://bugs.webkit.org/show_bug.cgi?id=46013 for details.
+ bool containing_view_has_focus_;
+
+ // True if NPP_New did not return an error.
+ bool creation_succeeded_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebPluginDelegateImpl);
+};
+
+#endif // WEBKIT_GLUE_PLUGINS_WEBPLUGIN_DELEGATE_IMPL_H_
diff --git a/webkit/glue/plugins/webplugin_delegate_impl_gtk.cc b/webkit/glue/plugins/webplugin_delegate_impl_gtk.cc
new file mode 100644
index 0000000..609b41e
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_delegate_impl_gtk.cc
@@ -0,0 +1,767 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webplugin_delegate_impl.h"
+
+#include <string>
+#include <vector>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "base/metrics/stats_counters.h"
+#include "base/string_util.h"
+#include "gfx/blit.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCursorInfo.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
+#include "webkit/glue/plugins/gtk_plugin_container.h"
+#include "webkit/glue/plugins/plugin_constants_win.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/glue/plugins/plugin_stream_url.h"
+#include "webkit/glue/plugins/webplugin.h"
+#include "webkit/glue/webkit_glue.h"
+
+#include "third_party/npapi/bindings/npapi_x11.h"
+
+using WebKit::WebCursorInfo;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebInputEvent;
+using WebKit::WebMouseEvent;
+
+WebPluginDelegateImpl::WebPluginDelegateImpl(
+ gfx::PluginWindowHandle containing_view,
+ NPAPI::PluginInstance *instance)
+ : windowed_handle_(0),
+ windowed_did_set_window_(false),
+ windowless_(false),
+ plugin_(NULL),
+ instance_(instance),
+ windowless_shm_pixmap_(None),
+ pixmap_(NULL),
+ first_event_time_(-1.0),
+ plug_(NULL),
+ socket_(NULL),
+ parent_(containing_view),
+ quirks_(0),
+ handle_event_depth_(0),
+ first_set_window_call_(true),
+ plugin_has_focus_(false),
+ has_webkit_focus_(false),
+ containing_view_has_focus_(true),
+ creation_succeeded_(false) {
+ memset(&window_, 0, sizeof(window_));
+ if (instance_->mime_type() == "application/x-shockwave-flash") {
+ // Flash is tied to Firefox's whacky behavior with windowless plugins. See
+ // comments in WindowlessPaint.
+ // TODO(viettrungluu): PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK: Don't allow
+ // right-clicks in windowless content since Flash 10.1 (initial release, at
+ // least) hangs in that case. Remove this once Flash is fixed.
+ quirks_ |= PLUGIN_QUIRK_WINDOWLESS_OFFSET_WINDOW_TO_DRAW
+ | PLUGIN_QUIRK_WINDOWLESS_INVALIDATE_AFTER_SET_WINDOW
+ | PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK;
+ }
+
+ // TODO(evanm): I played with this for quite a while but couldn't
+ // figure out a way to make Flash not crash unless I didn't call
+ // NPP_SetWindow.
+ // However, after piman's grand refactor of windowed plugins, maybe
+ // this is no longer necessary.
+ quirks_ |= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY;
+}
+
+WebPluginDelegateImpl::~WebPluginDelegateImpl() {
+ DestroyInstance();
+
+ if (!windowless_)
+ WindowedDestroyWindow();
+
+ if (window_.ws_info) {
+ // We only ever use ws_info as an NPSetWindowCallbackStruct.
+ delete static_cast<NPSetWindowCallbackStruct*>(window_.ws_info);
+ }
+
+ if (pixmap_) {
+ g_object_unref(pixmap_);
+ pixmap_ = NULL;
+ }
+}
+
+bool WebPluginDelegateImpl::PlatformInitialize() {
+ gfx::PluginWindowHandle handle =
+ windowless_ ? 0 : gtk_plug_get_id(GTK_PLUG(plug_));
+ plugin_->SetWindow(handle);
+ return true;
+}
+
+void WebPluginDelegateImpl::PlatformDestroyInstance() {
+ // Nothing to do here.
+}
+
+void WebPluginDelegateImpl::Paint(WebKit::WebCanvas* canvas,
+ const gfx::Rect& rect) {
+ if (!windowless_)
+ return;
+ cairo_t* context = canvas->beginPlatformPaint();
+ WindowlessPaint(context, rect);
+ canvas->endPlatformPaint();
+}
+
+void WebPluginDelegateImpl::Print(cairo_t* context) {
+ NOTIMPLEMENTED();
+}
+
+void WebPluginDelegateImpl::InstallMissingPlugin() {
+ NOTIMPLEMENTED();
+}
+
+bool WebPluginDelegateImpl::WindowedCreatePlugin() {
+ DCHECK(!windowed_handle_);
+ DCHECK(!plug_);
+
+ // NPP_GetValue() might write 4 bytes of data to this variable. Don't use a
+ // single byte bool, use an int instead and make sure it is initialized.
+ int xembed = 0;
+ NPError err = instance_->NPP_GetValue(NPPVpluginNeedsXEmbed, &xembed);
+ if (err != NPERR_NO_ERROR || !xembed) {
+ NOTIMPLEMENTED() << " windowed plugin but without xembed. "
+ "See http://code.google.com/p/chromium/issues/detail?id=38229";
+ return false;
+ }
+
+ // Passing 0 as the socket XID creates a plug without plugging it in a socket
+ // yet, so that it can be latter added with gtk_socket_add_id().
+ plug_ = gtk_plug_new(0);
+ gtk_widget_show(plug_);
+ socket_ = gtk_socket_new();
+ gtk_widget_show(socket_);
+ gtk_container_add(GTK_CONTAINER(plug_), socket_);
+ gtk_widget_show_all(plug_);
+
+ // Prevent the plug from being destroyed if the browser kills the container
+ // window.
+ g_signal_connect(plug_, "delete-event", G_CALLBACK(gtk_true), NULL);
+ // Prevent the socket from being destroyed when the plugin removes itself.
+ g_signal_connect(socket_, "plug_removed", G_CALLBACK(gtk_true), NULL);
+
+ windowed_handle_ = gtk_socket_get_id(GTK_SOCKET(socket_));
+
+ window_.window = reinterpret_cast<void*>(windowed_handle_);
+
+ if (!window_.ws_info)
+ window_.ws_info = new NPSetWindowCallbackStruct;
+ NPSetWindowCallbackStruct* extra =
+ static_cast<NPSetWindowCallbackStruct*>(window_.ws_info);
+ extra->display = GDK_DISPLAY();
+ extra->visual = DefaultVisual(GDK_DISPLAY(), 0);
+ extra->depth = DefaultDepth(GDK_DISPLAY(), 0);
+ extra->colormap = DefaultColormap(GDK_DISPLAY(), 0);
+
+ return true;
+}
+
+void WebPluginDelegateImpl::WindowedDestroyWindow() {
+ if (plug_) {
+ plugin_->WillDestroyWindow(gtk_plug_get_id(GTK_PLUG(plug_)));
+
+ gtk_widget_destroy(plug_);
+ plug_ = NULL;
+ socket_ = NULL;
+ windowed_handle_ = 0;
+ }
+}
+
+bool WebPluginDelegateImpl::WindowedReposition(
+ const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect) {
+ if (window_rect == window_rect_ && clip_rect == clip_rect_)
+ return false;
+
+ window_rect_ = window_rect;
+ clip_rect_ = clip_rect;
+
+ return true;
+}
+
+void WebPluginDelegateImpl::WindowedSetWindow() {
+ if (!instance_)
+ return;
+
+ if (!windowed_handle_) {
+ NOTREACHED();
+ return;
+ }
+
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=108347
+ // If we call NPP_SetWindow with a <= 0 width or height, problems arise in
+ // Flash (and possibly other plugins).
+ // TODO(piman): the Mozilla code suggests that for the Java plugin, we should
+ // still call NPP_SetWindow in that case. We need to verify that.
+ if (window_rect_.width() <= 0 || window_rect_.height() <= 0) {
+ return;
+ }
+
+ instance()->set_window_handle(windowed_handle_);
+
+ DCHECK(!instance()->windowless());
+
+ window_.clipRect.top = clip_rect_.y();
+ window_.clipRect.left = clip_rect_.x();
+ window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height();
+ window_.clipRect.right = clip_rect_.x() + clip_rect_.width();
+ window_.height = window_rect_.height();
+ window_.width = window_rect_.width();
+ window_.x = window_rect_.x();
+ window_.y = window_rect_.y();
+
+ //window_.window = windowed_handle_;
+ window_.type = NPWindowTypeWindow;
+
+ // Reset this flag before entering the instance in case of side-effects.
+ windowed_did_set_window_ = true;
+
+ NPError err = instance()->NPP_SetWindow(&window_);
+ DCHECK(err == NPERR_NO_ERROR);
+}
+
+void WebPluginDelegateImpl::WindowlessUpdateGeometry(
+ const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect) {
+ // Only resend to the instance if the geometry has changed.
+ if (window_rect == window_rect_ && clip_rect == clip_rect_)
+ return;
+
+ clip_rect_ = clip_rect;
+ window_rect_ = window_rect;
+ WindowlessSetWindow();
+}
+
+void WebPluginDelegateImpl::EnsurePixmapAtLeastSize(int width, int height) {
+ if (pixmap_) {
+ gint cur_width, cur_height;
+ gdk_drawable_get_size(pixmap_, &cur_width, &cur_height);
+ if (cur_width >= width && cur_height >= height)
+ return; // We are already the appropriate size.
+
+ // Otherwise, we need to recreate ourselves.
+ g_object_unref(pixmap_);
+ pixmap_ = NULL;
+ }
+
+ // |sys_visual| is owned by gdk; we shouldn't free it.
+ GdkVisual* sys_visual = gdk_visual_get_system();
+ pixmap_ = gdk_pixmap_new(NULL, // use width/height/depth params
+ std::max(1, width), std::max(1, height),
+ sys_visual->depth);
+ GdkColormap* colormap = gdk_colormap_new(gdk_visual_get_system(),
+ FALSE);
+ gdk_drawable_set_colormap(GDK_DRAWABLE(pixmap_), colormap);
+ // The GdkDrawable now owns the GdkColormap.
+ g_object_unref(colormap);
+}
+
+#ifdef DEBUG_RECTANGLES
+namespace {
+
+// Draw a rectangle on a Cairo context.
+// Useful for debugging various rectangles involved in drawing plugins.
+void DrawDebugRectangle(cairo_t* cairo,
+ const gfx::Rect& rect,
+ float r, float g, float b) {
+ cairo_set_source_rgba(cairo, r, g, b, 0.5);
+ cairo_rectangle(cairo, rect.x(), rect.y(),
+ rect.width(), rect.height());
+ cairo_stroke(cairo);
+}
+
+} // namespace
+#endif
+
+void WebPluginDelegateImpl::WindowlessPaint(cairo_t* context,
+ const gfx::Rect& damage_rect) {
+ // Compare to:
+ // http://mxr.mozilla.org/firefox/source/layout/generic/nsObjectFrame.cpp:
+ // nsPluginInstanceOwner::Renderer::NativeDraw().
+
+ DCHECK(context);
+
+ // TODO(darin): we should avoid calling NPP_SetWindow here since it may
+ // cause page layout to be invalidated.
+
+ // The actual dirty region is just the intersection of the plugin window and
+ // the clip window with the damage region. However, the plugin wants to draw
+ // relative to the containing window's origin, so our pixmap must be from the
+ // window's origin down to the bottom-right edge of the dirty region.
+ //
+ // Typical case:
+ // X-----------------------------------+-----------------------------+
+ // | | |
+ // | pixmap +-------------------+ |
+ // | | damage | window |
+ // | | | |
+ // | +---+-------------------+-------------+ |
+ // | | | | clip | |
+ // | +---+---+-------------------+----------+ | |
+ // | | | | | | | |
+ // | | | | draw | | | |
+ // | | | | | | | |
+ // +-------+---+---+-------------------+----------+--+ |
+ // | | | | | |
+ // | | +-------------------+ | |
+ // | | | |
+ // | | plugin | |
+ // | +--------------------------------------+ |
+ // | |
+ // | |
+ // +-----------------------------------------------------------------+
+ // X = origin
+ //
+ // NPAPI doesn't properly define which coordinates each of
+ // - window.clipRect, window.x and window.y in the SetWindow call
+ // - x and y in GraphicsExpose HandleEvent call
+ // are relative to, nor does it define what the pixmap is relative to.
+ //
+ // Any sane values for them just don't work with the flash plugin. Firefox
+ // has some interesting behavior. Experiments showed that:
+ // - window.clipRect is always in the same space as window.x and window.y
+ // - in the first SetWindow call, or when scrolling, window.x and window.y are
+ // the coordinates of the plugin relative to the window.
+ // - whenever only a part of the plugin is drawn, Firefox issues a SetWindow
+ // call before each GraphicsExpose event, that sets the drawing origin to
+ // (0, 0) as if the plugin was scrolled to be partially out of the view. The
+ // GraphicsExpose event has coordinates relative to the "window" (assuming
+ // that virtual scroll). The pixmap is also relative to the window. It always
+ // sets the clip rect to the draw rect.
+ //
+ // Attempts to deviate from that makes Flash render at the wrong place in the
+ // pixmap, or render the wrong pixels.
+ //
+ // Flash plugin:
+ // X-----------------------------------------------------------------+
+ // | |
+ // | +-------------------+ "real" window |
+ // | | damage | |
+ // | | | |
+ // | +---+-------------------+-------------+ |
+ // | | | | "real" clip | |
+ // | +---+---O===================#==========#==#===============#
+ // | | | H draw | | | H
+ // | | | H = pixmap | | | H
+ // | | | H = "apparent" clip | | | H
+ // | + +---#-------------------+----------+--+ H
+ // | | H | | H
+ // | | H-------------------+ | H
+ // | | H | H
+ // | | H plugin | H
+ // | +-------#------------------------------+ H
+ // | H H
+ // | H "apparent" window H
+ // +---------------#=================================================#
+ // X = "real" origin
+ // O = "apparent" origin
+ // "real" means as seen by Chrome
+ // "apparent" means as seen by the plugin.
+
+ gfx::Rect draw_rect = window_rect_.Intersect(damage_rect);
+
+ // clip_rect_ is relative to the plugin
+ gfx::Rect clip_rect_window = clip_rect_;
+ clip_rect_window.Offset(window_rect_.x(), window_rect_.y());
+ draw_rect = draw_rect.Intersect(clip_rect_window);
+
+ // These offsets represent by how much the view is shifted to accomodate
+ // Flash (the coordinates of X relative to O in the diagram above).
+ int offset_x = 0;
+ int offset_y = 0;
+ if (quirks_ & PLUGIN_QUIRK_WINDOWLESS_OFFSET_WINDOW_TO_DRAW) {
+ offset_x = -draw_rect.x();
+ offset_y = -draw_rect.y();
+ window_.clipRect.top = 0;
+ window_.clipRect.left = 0;
+ window_.clipRect.bottom = draw_rect.height();
+ window_.clipRect.right = draw_rect.width();
+ window_.height = window_rect_.height();
+ window_.width = window_rect_.width();
+ window_.x = window_rect_.x() - draw_rect.x();
+ window_.y = window_rect_.y() - draw_rect.y();
+ window_.type = NPWindowTypeDrawable;
+ DCHECK(window_.ws_info);
+ NPError err = instance()->NPP_SetWindow(&window_);
+ DCHECK_EQ(err, NPERR_NO_ERROR);
+ }
+
+ gfx::Rect pixmap_draw_rect = draw_rect;
+ pixmap_draw_rect.Offset(offset_x, offset_y);
+
+ gfx::Rect pixmap_rect(0, 0,
+ pixmap_draw_rect.right(),
+ pixmap_draw_rect.bottom());
+
+ // Construct the paint message, targeting the pixmap.
+ NPEvent np_event = {0};
+ XGraphicsExposeEvent &event = np_event.xgraphicsexpose;
+ event.type = GraphicsExpose;
+ event.x = pixmap_draw_rect.x();
+ event.y = pixmap_draw_rect.y();
+ event.width = pixmap_draw_rect.width();
+ event.height = pixmap_draw_rect.height();
+ event.display = GDK_DISPLAY();
+
+ if (windowless_shm_pixmap_ != None) {
+ Pixmap pixmap = None;
+ GC xgc = NULL;
+ Display* display = event.display;
+ gfx::Rect plugin_draw_rect = draw_rect;
+
+ // Make plugin_draw_rect relative to the plugin window.
+ plugin_draw_rect.Offset(-window_rect_.x(), -window_rect_.y());
+
+ // In case the drawing area does not start with the plugin window origin,
+ // we can not let the plugin directly draw over the shared memory pixmap.
+ if (plugin_draw_rect.x() != pixmap_draw_rect.x() ||
+ plugin_draw_rect.y() != pixmap_draw_rect.y()) {
+ pixmap = XCreatePixmap(display, windowless_shm_pixmap_,
+ std::max(1, pixmap_rect.width()),
+ std::max(1, pixmap_rect.height()),
+ DefaultDepth(display, 0));
+ xgc = XCreateGC(display, windowless_shm_pixmap_, 0, NULL);
+ // Copy the current image into the pixmap, so the plugin can draw over it.
+ XCopyArea(display, windowless_shm_pixmap_, pixmap, xgc,
+ plugin_draw_rect.x(), plugin_draw_rect.y(),
+ pixmap_draw_rect.width(), pixmap_draw_rect.height(),
+ pixmap_draw_rect.x(), pixmap_draw_rect.y());
+
+ event.drawable = pixmap;
+ } else {
+ event.drawable = windowless_shm_pixmap_;
+ }
+
+ // Tell the plugin to paint into the pixmap.
+ static base::StatsRate plugin_paint("Plugin.Paint");
+ base::StatsScope<base::StatsRate> scope(plugin_paint);
+ NPError err = instance()->NPP_HandleEvent(&np_event);
+ DCHECK_EQ(err, NPERR_NO_ERROR);
+
+ if (pixmap != None) {
+ // Copy the rendered image pixmap back into the shm pixmap
+ // and thus the drawing buffer.
+ XCopyArea(display, pixmap, windowless_shm_pixmap_, xgc,
+ pixmap_draw_rect.x(), pixmap_draw_rect.y(),
+ pixmap_draw_rect.width(), pixmap_draw_rect.height(),
+ plugin_draw_rect.x(), plugin_draw_rect.y());
+ XSync(display, FALSE);
+ if (xgc)
+ XFreeGC(display, xgc);
+ XFreePixmap(display, pixmap);
+ } else {
+ XSync(display, FALSE);
+ }
+ } else {
+ EnsurePixmapAtLeastSize(pixmap_rect.width(), pixmap_rect.height());
+
+ // Copy the current image into the pixmap, so the plugin can draw over
+ // this background.
+ cairo_t* cairo = gdk_cairo_create(pixmap_);
+ BlitContextToContext(cairo, pixmap_draw_rect, context, draw_rect.origin());
+ cairo_destroy(cairo);
+
+ event.drawable = GDK_PIXMAP_XID(pixmap_);
+
+ // Tell the plugin to paint into the pixmap.
+ static base::StatsRate plugin_paint("Plugin.Paint");
+ base::StatsScope<base::StatsRate> scope(plugin_paint);
+ NPError err = instance()->NPP_HandleEvent(&np_event);
+ DCHECK_EQ(err, NPERR_NO_ERROR);
+
+ cairo_save(context);
+ // Now copy the rendered image pixmap back into the drawing buffer.
+ gdk_cairo_set_source_pixmap(context, pixmap_, -offset_x, -offset_y);
+ cairo_rectangle(context, draw_rect.x(), draw_rect.y(),
+ draw_rect.width(), draw_rect.height());
+ cairo_clip(context);
+ cairo_paint(context);
+
+#ifdef DEBUG_RECTANGLES
+ // Draw some debugging rectangles.
+ // Pixmap rect = blue.
+ DrawDebugRectangle(context, pixmap_rect, 0, 0, 1);
+ // Drawing rect = red.
+ DrawDebugRectangle(context, draw_rect, 1, 0, 0);
+#endif
+ cairo_restore(context);
+ }
+}
+
+void WebPluginDelegateImpl::WindowlessSetWindow() {
+ if (!instance())
+ return;
+
+ if (window_rect_.IsEmpty()) // wait for geometry to be set.
+ return;
+
+ DCHECK(instance()->windowless());
+ // Mozilla docs say that this window param is not used for windowless
+ // plugins; rather, the window is passed during the GraphicsExpose event.
+ DCHECK(window_.window == 0);
+
+ window_.clipRect.top = clip_rect_.y() + window_rect_.y();
+ window_.clipRect.left = clip_rect_.x() + window_rect_.x();
+ window_.clipRect.bottom =
+ clip_rect_.y() + clip_rect_.height() + window_rect_.y();
+ window_.clipRect.right =
+ clip_rect_.x() + clip_rect_.width() + window_rect_.x();
+ window_.height = window_rect_.height();
+ window_.width = window_rect_.width();
+ window_.x = window_rect_.x();
+ window_.y = window_rect_.y();
+ window_.type = NPWindowTypeDrawable;
+
+ if (!window_.ws_info)
+ window_.ws_info = new NPSetWindowCallbackStruct;
+ NPSetWindowCallbackStruct* extra =
+ static_cast<NPSetWindowCallbackStruct*>(window_.ws_info);
+ extra->display = GDK_DISPLAY();
+ extra->visual = DefaultVisual(GDK_DISPLAY(), 0);
+ extra->depth = DefaultDepth(GDK_DISPLAY(), 0);
+ extra->colormap = DefaultColormap(GDK_DISPLAY(), 0);
+
+ NPError err = instance()->NPP_SetWindow(&window_);
+ DCHECK(err == NPERR_NO_ERROR);
+ if (quirks_ & PLUGIN_QUIRK_WINDOWLESS_INVALIDATE_AFTER_SET_WINDOW) {
+ // After a NPP_SetWindow, Flash cancels its timer that generates the
+ // invalidates until it gets a paint event, but doesn't explicitly call
+ // NPP_InvalidateRect.
+ plugin_->InvalidateRect(clip_rect_);
+ }
+}
+
+bool WebPluginDelegateImpl::PlatformSetPluginHasFocus(bool focused) {
+ DCHECK(instance()->windowless());
+
+ NPEvent np_event = {0};
+ XFocusChangeEvent &event = np_event.xfocus;
+ event.type = focused ? FocusIn : FocusOut;
+ event.display = GDK_DISPLAY();
+ // Same values as Firefox. .serial and .window stay 0.
+ event.mode = -1;
+ event.detail = NotifyDetailNone;
+ instance()->NPP_HandleEvent(&np_event);
+ return true;
+}
+
+// Converts a WebInputEvent::Modifiers bitfield into a
+// corresponding X modifier state.
+static int GetXModifierState(int modifiers) {
+ int x_state = 0;
+ if (modifiers & WebInputEvent::ControlKey)
+ x_state |= ControlMask;
+ if (modifiers & WebInputEvent::ShiftKey)
+ x_state |= ShiftMask;
+ if (modifiers & WebInputEvent::AltKey)
+ x_state |= Mod1Mask;
+ if (modifiers & WebInputEvent::MetaKey)
+ x_state |= Mod2Mask;
+ if (modifiers & WebInputEvent::LeftButtonDown)
+ x_state |= Button1Mask;
+ if (modifiers & WebInputEvent::MiddleButtonDown)
+ x_state |= Button2Mask;
+ if (modifiers & WebInputEvent::RightButtonDown)
+ x_state |= Button3Mask;
+ // TODO(piman@google.com): There are other modifiers, e.g. Num Lock, that
+ // should be set (and Firefox does), but we didn't keep the information in
+ // the WebKit event.
+ return x_state;
+}
+
+static bool NPEventFromWebMouseEvent(const WebMouseEvent& event,
+ Time timestamp,
+ NPEvent *np_event) {
+ np_event->xany.display = GDK_DISPLAY();
+ // NOTE: Firefox keeps xany.serial and xany.window as 0.
+
+ int modifier_state = GetXModifierState(event.modifiers);
+
+ Window root = GDK_ROOT_WINDOW();
+ switch (event.type) {
+ case WebInputEvent::MouseMove: {
+ np_event->type = MotionNotify;
+ XMotionEvent &motion_event = np_event->xmotion;
+ motion_event.root = root;
+ motion_event.time = timestamp;
+ motion_event.x = event.x;
+ motion_event.y = event.y;
+ motion_event.x_root = event.globalX;
+ motion_event.y_root = event.globalY;
+ motion_event.state = modifier_state;
+ motion_event.is_hint = NotifyNormal;
+ motion_event.same_screen = True;
+ break;
+ }
+ case WebInputEvent::MouseLeave:
+ case WebInputEvent::MouseEnter: {
+ if (event.type == WebInputEvent::MouseEnter) {
+ np_event->type = EnterNotify;
+ } else {
+ np_event->type = LeaveNotify;
+ }
+ XCrossingEvent &crossing_event = np_event->xcrossing;
+ crossing_event.root = root;
+ crossing_event.time = timestamp;
+ crossing_event.x = event.x;
+ crossing_event.y = event.y;
+ crossing_event.x_root = event.globalX;
+ crossing_event.y_root = event.globalY;
+ crossing_event.mode = -1; // This is what Firefox sets it to.
+ crossing_event.detail = NotifyDetailNone;
+ crossing_event.same_screen = True;
+ // TODO(piman@google.com): set this to the correct value. Firefox does. I
+ // don't know where to get the information though, we get focus
+ // notifications, but no unfocus.
+ crossing_event.focus = 0;
+ crossing_event.state = modifier_state;
+ break;
+ }
+ case WebInputEvent::MouseUp:
+ case WebInputEvent::MouseDown: {
+ if (event.type == WebInputEvent::MouseDown) {
+ np_event->type = ButtonPress;
+ } else {
+ np_event->type = ButtonRelease;
+ }
+ XButtonEvent &button_event = np_event->xbutton;
+ button_event.root = root;
+ button_event.time = timestamp;
+ button_event.x = event.x;
+ button_event.y = event.y;
+ button_event.x_root = event.globalX;
+ button_event.y_root = event.globalY;
+ button_event.state = modifier_state;
+ switch (event.button) {
+ case WebMouseEvent::ButtonLeft:
+ button_event.button = Button1;
+ break;
+ case WebMouseEvent::ButtonMiddle:
+ button_event.button = Button2;
+ break;
+ case WebMouseEvent::ButtonRight:
+ button_event.button = Button3;
+ break;
+ default:
+ NOTREACHED();
+ }
+ button_event.same_screen = True;
+ break;
+ }
+ default:
+ NOTREACHED();
+ return false;
+ }
+ return true;
+}
+
+static bool NPEventFromWebKeyboardEvent(const WebKeyboardEvent& event,
+ Time timestamp,
+ NPEvent *np_event) {
+ np_event->xany.display = GDK_DISPLAY();
+ // NOTE: Firefox keeps xany.serial and xany.window as 0.
+
+ switch (event.type) {
+ case WebKeyboardEvent::KeyDown:
+ np_event->type = KeyPress;
+ break;
+ case WebKeyboardEvent::KeyUp:
+ np_event->type = KeyRelease;
+ break;
+ default:
+ NOTREACHED();
+ return false;
+ }
+ XKeyEvent &key_event = np_event->xkey;
+ key_event.send_event = False;
+ key_event.display = GDK_DISPLAY();
+ // NOTE: Firefox keeps xany.serial and xany.window as 0.
+ // TODO(piman@google.com): is this right for multiple screens ?
+ key_event.root = DefaultRootWindow(key_event.display);
+ key_event.time = timestamp;
+ // NOTE: We don't have the correct information for x/y/x_root/y_root. Firefox
+ // doesn't have it either, so we pass the same values.
+ key_event.x = 0;
+ key_event.y = 0;
+ key_event.x_root = -1;
+ key_event.y_root = -1;
+ key_event.state = GetXModifierState(event.modifiers);
+ key_event.keycode = event.nativeKeyCode;
+ key_event.same_screen = True;
+ return true;
+}
+
+static bool NPEventFromWebInputEvent(const WebInputEvent& event,
+ Time timestamp,
+ NPEvent* np_event) {
+ switch (event.type) {
+ case WebInputEvent::MouseMove:
+ case WebInputEvent::MouseLeave:
+ case WebInputEvent::MouseEnter:
+ case WebInputEvent::MouseDown:
+ case WebInputEvent::MouseUp:
+ if (event.size < sizeof(WebMouseEvent)) {
+ NOTREACHED();
+ return false;
+ }
+ return NPEventFromWebMouseEvent(
+ *static_cast<const WebMouseEvent*>(&event), timestamp, np_event);
+ case WebInputEvent::KeyDown:
+ case WebInputEvent::KeyUp:
+ if (event.size < sizeof(WebKeyboardEvent)) {
+ NOTREACHED();
+ return false;
+ }
+ return NPEventFromWebKeyboardEvent(
+ *static_cast<const WebKeyboardEvent*>(&event), timestamp, np_event);
+ default:
+ return false;
+ }
+}
+
+bool WebPluginDelegateImpl::PlatformHandleInputEvent(
+ const WebInputEvent& event, WebCursorInfo* cursor_info) {
+
+ if (first_event_time_ < 0.0)
+ first_event_time_ = event.timeStampSeconds;
+ Time timestamp = static_cast<Time>(
+ (event.timeStampSeconds - first_event_time_) * 1.0e3);
+ NPEvent np_event = {0};
+ if (!NPEventFromWebInputEvent(event, timestamp, &np_event)) {
+ return false;
+ }
+ // See comment about PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK in constructor.
+ if (windowless_ &&
+ (quirks_ & PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK) &&
+ (np_event.type == ButtonPress || np_event.type == ButtonRelease) &&
+ (np_event.xbutton.button == Button3)) {
+ return false;
+ }
+
+ bool ret = instance()->NPP_HandleEvent(&np_event) != 0;
+
+ // Flash always returns false, even when the event is handled.
+ ret = true;
+
+#if 0
+ if (event->event == WM_MOUSEMOVE) {
+ // Snag a reference to the current cursor ASAP in case the plugin modified
+ // it. There is a nasty race condition here with the multiprocess browser
+ // as someone might be setting the cursor in the main process as well.
+ *cursor = current_windowless_cursor_;
+ }
+#endif
+
+ return ret;
+}
diff --git a/webkit/glue/plugins/webplugin_delegate_impl_mac.mm b/webkit/glue/plugins/webplugin_delegate_impl_mac.mm
new file mode 100644
index 0000000..552484a
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_delegate_impl_mac.mm
@@ -0,0 +1,1145 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Cocoa/Cocoa.h>
+#import <QuartzCore/QuartzCore.h>
+
+#include "webkit/glue/plugins/webplugin_delegate_impl.h"
+
+#include <string>
+#include <unistd.h>
+#include <set>
+
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/metrics/stats_counters.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "base/sys_string_conversions.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/glue/plugins/plugin_stream_url.h"
+#include "webkit/glue/plugins/plugin_web_event_converter_mac.h"
+#include "webkit/glue/plugins/webplugin.h"
+#include "webkit/glue/plugins/webplugin_accelerated_surface_mac.h"
+#include "webkit/glue/webkit_glue.h"
+
+#ifndef NP_NO_CARBON
+#include "webkit/glue/plugins/carbon_plugin_window_tracker_mac.h"
+#endif
+
+#ifndef NP_NO_QUICKDRAW
+#include "webkit/glue/plugins/quickdraw_drawing_manager_mac.h"
+#endif
+
+using webkit_glue::WebPlugin;
+using webkit_glue::WebPluginDelegate;
+using webkit_glue::WebPluginResourceClient;
+using WebKit::WebCursorInfo;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebInputEvent;
+using WebKit::WebMouseEvent;
+using WebKit::WebMouseWheelEvent;
+
+const int kCoreAnimationRedrawPeriodMs = 10; // 100 Hz
+
+// Important implementation notes: The Mac definition of NPAPI, particularly
+// the distinction between windowed and windowless modes, differs from the
+// Windows and Linux definitions. Most of those differences are
+// accomodated by the WebPluginDelegate class.
+
+namespace {
+
+WebPluginDelegateImpl* g_active_delegate;
+
+// Helper to simplify correct usage of g_active_delegate. Instantiating will
+// set the active delegate to |delegate| for the lifetime of the object, then
+// NULL when it goes out of scope.
+class ScopedActiveDelegate {
+public:
+ explicit ScopedActiveDelegate(WebPluginDelegateImpl* delegate) {
+ g_active_delegate = delegate;
+ }
+ ~ScopedActiveDelegate() {
+ g_active_delegate = NULL;
+ }
+private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedActiveDelegate);
+};
+
+#ifndef NP_NO_CARBON
+// Timer periods for sending idle events to Carbon plugins. The visible value
+// (50Hz) matches both Safari and Firefox. The hidden value (8Hz) matches
+// Firefox; according to https://bugzilla.mozilla.org/show_bug.cgi?id=525533
+// going lower than that causes issues.
+const int kVisibleIdlePeriodMs = 20; // (50Hz)
+const int kHiddenIdlePeriodMs = 125; // (8Hz)
+
+class CarbonIdleEventSource {
+ public:
+ // Returns the shared Carbon idle event source.
+ static CarbonIdleEventSource* SharedInstance() {
+ DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI);
+ static CarbonIdleEventSource* event_source = new CarbonIdleEventSource();
+ return event_source;
+ }
+
+ // Registers the plugin delegate as interested in receiving idle events at
+ // a rate appropriate for the given visibility. A delegate can safely be
+ // re-registered any number of times, with the latest registration winning.
+ void RegisterDelegate(WebPluginDelegateImpl* delegate, bool visible) {
+ if (visible) {
+ visible_delegates_->RegisterDelegate(delegate);
+ hidden_delegates_->UnregisterDelegate(delegate);
+ } else {
+ hidden_delegates_->RegisterDelegate(delegate);
+ visible_delegates_->UnregisterDelegate(delegate);
+ }
+ }
+
+ // Removes the plugin delegate from the list of plugins receiving idle events.
+ void UnregisterDelegate(WebPluginDelegateImpl* delegate) {
+ visible_delegates_->UnregisterDelegate(delegate);
+ hidden_delegates_->UnregisterDelegate(delegate);
+ }
+
+ private:
+ class VisibilityGroup {
+ public:
+ explicit VisibilityGroup(int timer_period)
+ : timer_period_(timer_period), iterator_(delegates_.end()) {}
+
+ // Adds |delegate| to this visibility group.
+ void RegisterDelegate(WebPluginDelegateImpl* delegate) {
+ if (delegates_.empty()) {
+ timer_.Start(base::TimeDelta::FromMilliseconds(timer_period_),
+ this, &VisibilityGroup::SendIdleEvents);
+ }
+ delegates_.insert(delegate);
+ }
+
+ // Removes |delegate| from this visibility group.
+ void UnregisterDelegate(WebPluginDelegateImpl* delegate) {
+ // If a plugin changes visibility during idle event handling, it
+ // may be removed from this set while SendIdleEvents is still iterating;
+ // if that happens and it's next on the list, increment the iterator
+ // before erasing so that the iteration won't be corrupted.
+ if ((iterator_ != delegates_.end()) && (*iterator_ == delegate))
+ ++iterator_;
+ size_t removed = delegates_.erase(delegate);
+ if (removed > 0 && delegates_.empty())
+ timer_.Stop();
+ }
+
+ private:
+ // Fires off idle events for each delegate in the group.
+ void SendIdleEvents() {
+ for (iterator_ = delegates_.begin(); iterator_ != delegates_.end();) {
+ // Pre-increment so that the skip logic in UnregisterDelegates works.
+ WebPluginDelegateImpl* delegate = *(iterator_++);
+ delegate->FireIdleEvent();
+ }
+ }
+
+ int timer_period_;
+ base::RepeatingTimer<VisibilityGroup> timer_;
+ std::set<WebPluginDelegateImpl*> delegates_;
+ std::set<WebPluginDelegateImpl*>::iterator iterator_;
+ };
+
+ CarbonIdleEventSource()
+ : visible_delegates_(new VisibilityGroup(kVisibleIdlePeriodMs)),
+ hidden_delegates_(new VisibilityGroup(kHiddenIdlePeriodMs)) {}
+
+ scoped_ptr<VisibilityGroup> visible_delegates_;
+ scoped_ptr<VisibilityGroup> hidden_delegates_;
+
+ DISALLOW_COPY_AND_ASSIGN(CarbonIdleEventSource);
+};
+#endif // !NP_NO_CARBON
+
+} // namespace
+
+// Helper to build and maintain a model of a drag entering the plugin but not
+// starting there. See explanation in PlatformHandleInputEvent.
+class ExternalDragTracker {
+ public:
+ ExternalDragTracker() : pressed_buttons_(0) {}
+
+ // Returns true if an external drag is in progress.
+ bool IsDragInProgress() { return pressed_buttons_ != 0; };
+
+ // Returns true if the given event appears to be related to an external drag.
+ bool EventIsRelatedToDrag(const WebInputEvent& event);
+
+ // Updates the tracking of whether an external drag is in progress--and if
+ // so what buttons it involves--based on the given event.
+ void UpdateDragStateFromEvent(const WebInputEvent& event);
+
+ private:
+ // Returns the mask for just the button state in a WebInputEvent's modifiers.
+ static int WebEventButtonModifierMask();
+
+ // The WebInputEvent modifier flags for any buttons that were down when an
+ // external drag entered the plugin, and which and are still down now.
+ int pressed_buttons_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExternalDragTracker);
+};
+
+void ExternalDragTracker::UpdateDragStateFromEvent(const WebInputEvent& event) {
+ switch (event.type) {
+ case WebInputEvent::MouseEnter:
+ pressed_buttons_ = event.modifiers & WebEventButtonModifierMask();
+ break;
+ case WebInputEvent::MouseUp: {
+ const WebMouseEvent* mouse_event =
+ static_cast<const WebMouseEvent*>(&event);
+ if (mouse_event->button == WebMouseEvent::ButtonLeft)
+ pressed_buttons_ &= ~WebInputEvent::LeftButtonDown;
+ if (mouse_event->button == WebMouseEvent::ButtonMiddle)
+ pressed_buttons_ &= ~WebInputEvent::MiddleButtonDown;
+ if (mouse_event->button == WebMouseEvent::ButtonRight)
+ pressed_buttons_ &= ~WebInputEvent::RightButtonDown;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+bool ExternalDragTracker::EventIsRelatedToDrag(const WebInputEvent& event) {
+ const WebMouseEvent* mouse_event = static_cast<const WebMouseEvent*>(&event);
+ switch (event.type) {
+ case WebInputEvent::MouseUp:
+ // We only care about release of buttons that were part of the drag.
+ return ((mouse_event->button == WebMouseEvent::ButtonLeft &&
+ (pressed_buttons_ & WebInputEvent::LeftButtonDown)) ||
+ (mouse_event->button == WebMouseEvent::ButtonMiddle &&
+ (pressed_buttons_ & WebInputEvent::MiddleButtonDown)) ||
+ (mouse_event->button == WebMouseEvent::ButtonRight &&
+ (pressed_buttons_ & WebInputEvent::RightButtonDown)));
+ case WebInputEvent::MouseEnter:
+ return (event.modifiers & WebEventButtonModifierMask()) != 0;
+ case WebInputEvent::MouseLeave:
+ case WebInputEvent::MouseMove: {
+ int event_buttons = (event.modifiers & WebEventButtonModifierMask());
+ return (pressed_buttons_ &&
+ pressed_buttons_ == event_buttons);
+ }
+ default:
+ return false;
+ }
+ return false;
+}
+
+int ExternalDragTracker::WebEventButtonModifierMask() {
+ return WebInputEvent::LeftButtonDown |
+ WebInputEvent::RightButtonDown |
+ WebInputEvent::MiddleButtonDown;
+}
+
+#pragma mark -
+#pragma mark Core WebPluginDelegate implementation
+
+WebPluginDelegateImpl::WebPluginDelegateImpl(
+ gfx::PluginWindowHandle containing_view,
+ NPAPI::PluginInstance *instance)
+ : windowed_handle_(NULL),
+ // all Mac plugins are "windowless" in the Windows/X11 sense
+ windowless_(true),
+ plugin_(NULL),
+ instance_(instance),
+ parent_(containing_view),
+ quirks_(0),
+ buffer_context_(NULL),
+ layer_(nil),
+ surface_(NULL),
+ renderer_(nil),
+ containing_window_has_focus_(false),
+ initial_window_focus_(false),
+ container_is_visible_(false),
+ have_called_set_window_(false),
+ ime_enabled_(false),
+ external_drag_tracker_(new ExternalDragTracker()),
+ handle_event_depth_(0),
+ first_set_window_call_(true),
+ plugin_has_focus_(false),
+ has_webkit_focus_(false),
+ containing_view_has_focus_(true),
+ creation_succeeded_(false) {
+ memset(&window_, 0, sizeof(window_));
+#ifndef NP_NO_CARBON
+ memset(&np_cg_context_, 0, sizeof(np_cg_context_));
+#endif
+#ifndef NP_NO_QUICKDRAW
+ memset(&qd_port_, 0, sizeof(qd_port_));
+#endif
+ instance->set_windowless(true);
+}
+
+WebPluginDelegateImpl::~WebPluginDelegateImpl() {
+ DestroyInstance();
+
+#ifndef NP_NO_CARBON
+ if (np_cg_context_.window) {
+ CarbonPluginWindowTracker::SharedInstance()->DestroyDummyWindowForDelegate(
+ this, reinterpret_cast<WindowRef>(np_cg_context_.window));
+ }
+#endif
+}
+
+bool WebPluginDelegateImpl::PlatformInitialize() {
+ // Don't set a NULL window handle on destroy for Mac plugins. This matches
+ // Safari and other Mac browsers (see PluginView::stop() in PluginView.cpp,
+ // where code to do so is surrounded by an #ifdef that excludes Mac OS X, or
+ // destroyPlugin in WebNetscapePluginView.mm, for examples).
+ quirks_ |= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY;
+
+ // Mac plugins don't expect to be unloaded, and they don't always do so
+ // cleanly, so don't unload them at shutdown.
+ instance()->plugin_lib()->PreventLibraryUnload();
+
+#ifndef NP_NO_QUICKDRAW
+ if (instance()->drawing_model() == NPDrawingModelQuickDraw) {
+ // For some QuickDraw plugins, we can sometimes get away with giving them
+ // a port pointing to a pixel buffer instead of a our actual dummy window.
+ // This gives us much better frame rates, because the window scraping we
+ // normally use is very slow.
+ // This breaks down if the plugin does anything complicated with the port
+ // (as QuickTime seems to during event handling, and sometimes when painting
+ // its controls), so we switch on the fly as necessary. (It might be
+ // possible to interpose sufficiently that we wouldn't have to switch back
+ // and forth, but the current approach gets us most of the benefit.)
+ // We can't do this at all with plugins that bypass the port entirely and
+ // attaches their own surface to the window.
+ // TODO(stuartmorgan): Test other QuickDraw plugins that we support and
+ // see if any others can use the fast path.
+ const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info();
+ if (plugin_info.name.find(ASCIIToUTF16("QuickTime")) != string16::npos)
+ quirks_ |= PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH;
+ }
+#endif
+
+#ifndef NP_NO_CARBON
+ if (instance()->event_model() == NPEventModelCarbon) {
+ // Create a stand-in for the browser window so that the plugin will have
+ // a non-NULL WindowRef to which it can refer.
+ CarbonPluginWindowTracker* window_tracker =
+ CarbonPluginWindowTracker::SharedInstance();
+ np_cg_context_.window = window_tracker->CreateDummyWindowForDelegate(this);
+ np_cg_context_.context = NULL;
+ UpdateDummyWindowBounds(gfx::Point(0, 0));
+ }
+#endif
+
+ NPDrawingModel drawing_model = instance()->drawing_model();
+ switch (drawing_model) {
+#ifndef NP_NO_QUICKDRAW
+ case NPDrawingModelQuickDraw:
+ if (instance()->event_model() != NPEventModelCarbon)
+ return false;
+ qd_manager_.reset(new QuickDrawDrawingManager());
+ qd_manager_->SetPluginWindow(
+ reinterpret_cast<WindowRef>(np_cg_context_.window));
+ qd_port_.port = qd_manager_->port();
+ window_.window = &qd_port_;
+ window_.type = NPWindowTypeDrawable;
+ break;
+#endif
+ case NPDrawingModelCoreGraphics:
+#ifndef NP_NO_CARBON
+ if (instance()->event_model() == NPEventModelCarbon)
+ window_.window = &np_cg_context_;
+#endif
+ window_.type = NPWindowTypeDrawable;
+ break;
+ case NPDrawingModelCoreAnimation:
+ case NPDrawingModelInvalidatingCoreAnimation: {
+ if (instance()->event_model() != NPEventModelCocoa)
+ return false;
+ window_.type = NPWindowTypeDrawable;
+ // Ask the plug-in for the CALayer it created for rendering content.
+ // Create a surface to host it, and request a "window" handle to identify
+ // the surface.
+ CALayer* layer = nil;
+ NPError err = instance()->NPP_GetValue(NPPVpluginCoreAnimationLayer,
+ reinterpret_cast<void*>(&layer));
+ if (!err) {
+ if (drawing_model == NPDrawingModelCoreAnimation) {
+ // Create the timer; it will be started when we get a window handle.
+ redraw_timer_.reset(new base::RepeatingTimer<WebPluginDelegateImpl>);
+ }
+ layer_ = layer;
+ surface_ = plugin_->GetAcceleratedSurface();
+
+ // If surface initialization fails for some reason, just continue
+ // without any drawing; returning false would be a more confusing user
+ // experience (since it triggers a missing plugin placeholder).
+ if (surface_->context()) {
+ renderer_ = [[CARenderer rendererWithCGLContext:surface_->context()
+ options:NULL] retain];
+ [renderer_ setLayer:layer_];
+ }
+ plugin_->BindFakePluginWindowHandle(false);
+ }
+ break;
+ }
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ // Let the WebPlugin know that we are windowless (unless this is a
+ // Core Animation plugin, in which case BindFakePluginWindowHandle will take
+ // care of setting up the appropriate window handle).
+ if (!layer_)
+ plugin_->SetWindow(NULL);
+
+#ifndef NP_NO_CARBON
+ // If the plugin wants Carbon events, hook up to the source of idle events.
+ if (instance()->event_model() == NPEventModelCarbon)
+ UpdateIdleEventRate();
+#endif
+
+ // QuickTime (in QD mode only) can crash if it gets other calls (e.g.,
+ // NPP_Write) before it gets a SetWindow call, so call SetWindow (with a 0x0
+ // rect) immediately.
+#ifndef NP_NO_QUICKDRAW
+ if (instance()->drawing_model() == NPDrawingModelQuickDraw) {
+ const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info();
+ if (plugin_info.name.find(ASCIIToUTF16("QuickTime")) != string16::npos)
+ WindowlessSetWindow();
+ }
+#endif
+
+ return true;
+}
+
+void WebPluginDelegateImpl::PlatformDestroyInstance() {
+#ifndef NP_NO_CARBON
+ if (instance()->event_model() == NPEventModelCarbon)
+ CarbonIdleEventSource::SharedInstance()->UnregisterDelegate(this);
+#endif
+ if (redraw_timer_.get())
+ redraw_timer_->Stop();
+ [renderer_ release];
+ renderer_ = nil;
+ layer_ = nil;
+}
+
+void WebPluginDelegateImpl::UpdateGeometryAndContext(
+ const gfx::Rect& window_rect, const gfx::Rect& clip_rect,
+ CGContextRef context) {
+ buffer_context_ = context;
+#ifndef NP_NO_CARBON
+ if (instance()->event_model() == NPEventModelCarbon) {
+ // Update the structure that is passed to Carbon+CoreGraphics plugins in
+ // NPP_SetWindow before calling UpdateGeometry, since that will trigger an
+ // NPP_SetWindow call if the geometry changes (which is the only time the
+ // context would be different), and some plugins (e.g., Flash) have an
+ // internal cache of the context that they only update when NPP_SetWindow
+ // is called.
+ np_cg_context_.context = context;
+ }
+#endif
+#ifndef NP_NO_QUICKDRAW
+ if (instance()->drawing_model() == NPDrawingModelQuickDraw)
+ qd_manager_->SetTargetContext(context, window_rect.size());
+#endif
+ UpdateGeometry(window_rect, clip_rect);
+}
+
+void WebPluginDelegateImpl::Paint(CGContextRef context, const gfx::Rect& rect) {
+ WindowlessPaint(context, rect);
+
+#ifndef NP_NO_QUICKDRAW
+ // Paint events are our cue to dump the current plugin bits into the buffer
+ // context if we are dealing with a QuickDraw plugin.
+ if (instance()->drawing_model() == NPDrawingModelQuickDraw) {
+ qd_manager_->UpdateContext();
+ }
+#endif
+}
+
+void WebPluginDelegateImpl::Print(CGContextRef context) {
+ NOTIMPLEMENTED();
+}
+
+bool WebPluginDelegateImpl::PlatformHandleInputEvent(
+ const WebInputEvent& event, WebCursorInfo* cursor_info) {
+ DCHECK(cursor_info != NULL);
+
+ // If we get an event before we've set up the plugin, bail.
+ if (!have_called_set_window_)
+ return false;
+#ifndef NP_NO_CARBON
+ if (instance()->event_model() == NPEventModelCarbon &&
+ !np_cg_context_.context) {
+ return false;
+ }
+#endif
+
+ if (WebInputEvent::isMouseEventType(event.type) ||
+ event.type == WebInputEvent::MouseWheel) {
+ // Check our plugin location before we send the event to the plugin, just
+ // in case we somehow missed a plugin frame change.
+ const WebMouseEvent* mouse_event =
+ static_cast<const WebMouseEvent*>(&event);
+ gfx::Point content_origin(
+ mouse_event->globalX - mouse_event->x - window_rect_.x(),
+ mouse_event->globalY - mouse_event->y - window_rect_.y());
+ if (content_origin.x() != content_area_origin_.x() ||
+ content_origin.y() != content_area_origin_.y()) {
+ DLOG(WARNING) << "Stale plugin content area location: "
+ << content_area_origin_ << " instead of "
+ << content_origin;
+ SetContentAreaOrigin(content_origin);
+ }
+
+ current_windowless_cursor_.GetCursorInfo(cursor_info);
+ }
+
+#ifndef NP_NO_CARBON
+ if (instance()->event_model() == NPEventModelCarbon) {
+#ifndef NP_NO_QUICKDRAW
+ if (instance()->drawing_model() == NPDrawingModelQuickDraw) {
+ if (quirks_ & PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH) {
+ // Mouse event handling doesn't work correctly in the fast path mode,
+ // so any time we get a mouse event turn the fast path off, but set a
+ // time to switch it on again (we don't rely just on MouseLeave because
+ // we don't want poor performance in the case of clicking the play
+ // button and then leaving the mouse there).
+ // This isn't perfect (specifically, click-and-hold doesn't seem to work
+ // if the fast path is on), but the slight regression is worthwhile
+ // for the improved framerates.
+ if (WebInputEvent::isMouseEventType(event.type)) {
+ if (event.type == WebInputEvent::MouseLeave) {
+ SetQuickDrawFastPathEnabled(true);
+ } else {
+ SetQuickDrawFastPathEnabled(false);
+ }
+ // Make sure the plugin wasn't destroyed during the switch.
+ if (!instance())
+ return false;
+ }
+ }
+
+ qd_manager_->MakePortCurrent();
+ }
+#endif
+
+ if (event.type == WebInputEvent::MouseMove) {
+ return true; // The recurring FireIdleEvent will send null events.
+ }
+ }
+#endif
+
+ ScopedActiveDelegate active_delegate(this);
+
+ // Create the plugin event structure.
+ NPEventModel event_model = instance()->event_model();
+ scoped_ptr<PluginWebEventConverter> event_converter(
+ PluginWebEventConverterFactory::CreateConverterForModel(event_model));
+ if (!(event_converter.get() && event_converter->InitWithEvent(event))) {
+ // Silently consume any keyboard event types that we don't handle, so that
+ // they don't fall through to the page.
+ if (WebInputEvent::isKeyboardEventType(event.type))
+ return true;
+ return false;
+ }
+ void* plugin_event = event_converter->plugin_event();
+
+ if (instance()->event_model() == NPEventModelCocoa) {
+ // We recieve events related to drags starting outside the plugin, but the
+ // NPAPI Cocoa event model spec says plugins shouldn't receive them, so
+ // filter them out.
+ // If we add a page capture mode at the WebKit layer (like the plugin
+ // capture mode that handles drags starting inside) this can be removed.
+ bool drag_related = external_drag_tracker_->EventIsRelatedToDrag(event);
+ external_drag_tracker_->UpdateDragStateFromEvent(event);
+ if (drag_related) {
+ if (event.type == WebInputEvent::MouseUp &&
+ !external_drag_tracker_->IsDragInProgress()) {
+ // When an external drag ends, we need to synthesize a MouseEntered.
+ NPCocoaEvent enter_event = *(static_cast<NPCocoaEvent*>(plugin_event));
+ enter_event.type = NPCocoaEventMouseEntered;
+ NPAPI::ScopedCurrentPluginEvent event_scope(instance(), &enter_event);
+ instance()->NPP_HandleEvent(&enter_event);
+ }
+ return false;
+ }
+ }
+
+ // Send the plugin the event.
+ scoped_ptr<NPAPI::ScopedCurrentPluginEvent> event_scope(NULL);
+ if (instance()->event_model() == NPEventModelCocoa) {
+ event_scope.reset(new NPAPI::ScopedCurrentPluginEvent(
+ instance(), static_cast<NPCocoaEvent*>(plugin_event)));
+ }
+ int16_t handle_response = instance()->NPP_HandleEvent(plugin_event);
+ bool handled = handle_response != kNPEventNotHandled;
+
+ if (handled && event.type == WebInputEvent::KeyDown) {
+ // Update IME state as requested by the plugin.
+ SetImeEnabled(handle_response == kNPEventStartIME);
+ }
+
+ // Plugins don't give accurate information about whether or not they handled
+ // events, so browsers on the Mac ignore the return value.
+ // Scroll events are the exception, since the Cocoa spec defines a meaning
+ // for the return value.
+ if (WebInputEvent::isMouseEventType(event.type)) {
+ handled = true;
+ } else if (WebInputEvent::isKeyboardEventType(event.type)) {
+ // For Command-key events, trust the return value since eating all menu
+ // shortcuts is not ideal.
+ // TODO(stuartmorgan): Implement the advanced key handling spec, and trust
+ // trust the key event return value from plugins that implement it.
+ if (!(event.modifiers & WebInputEvent::MetaKey))
+ handled = true;
+ }
+
+ return handled;
+}
+
+void WebPluginDelegateImpl::InstallMissingPlugin() {
+ NOTIMPLEMENTED();
+}
+
+#pragma mark -
+
+void WebPluginDelegateImpl::WindowlessUpdateGeometry(
+ const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect) {
+ gfx::Rect old_clip_rect = clip_rect_;
+ cached_clip_rect_ = clip_rect;
+ if (container_is_visible_) // Remove check when cached_clip_rect_ is removed.
+ clip_rect_ = clip_rect;
+ bool clip_rect_changed = (clip_rect_ != old_clip_rect);
+ bool window_size_changed = (window_rect.size() != window_rect_.size());
+
+ bool force_set_window = false;
+#ifndef NP_NO_QUICKDRAW
+ // In a QuickDraw plugin, a geometry update might have caused a port change;
+ // if so, we need to call SetWindow even if nothing else changed.
+ if (qd_manager_.get() && (qd_port_.port != qd_manager_->port())) {
+ qd_port_.port = qd_manager_->port();
+ force_set_window = true;
+ }
+#endif
+
+ if (window_rect == window_rect_ && !clip_rect_changed && !force_set_window)
+ return;
+
+ if (old_clip_rect.IsEmpty() != clip_rect_.IsEmpty()) {
+ PluginVisibilityChanged();
+ }
+
+ SetPluginRect(window_rect);
+
+#ifndef NP_NO_QUICKDRAW
+ if (window_size_changed && qd_manager_.get() &&
+ qd_manager_->IsFastPathEnabled()) {
+ // If the window size has changed, we need to turn off the fast path so that
+ // the full redraw goes to the window and we get a correct baseline paint.
+ SetQuickDrawFastPathEnabled(false);
+ return; // SetQuickDrawFastPathEnabled will call SetWindow for us.
+ }
+#endif
+
+ if (window_size_changed || clip_rect_changed || force_set_window)
+ WindowlessSetWindow();
+}
+
+void WebPluginDelegateImpl::WindowlessPaint(gfx::NativeDrawingContext context,
+ const gfx::Rect& damage_rect) {
+ // If we get a paint event before we are completely set up (e.g., a nested
+ // call while the plugin is still in NPP_SetWindow), bail.
+ if (!have_called_set_window_ || !buffer_context_)
+ return;
+ DCHECK(buffer_context_ == context);
+
+ static base::StatsRate plugin_paint("Plugin.Paint");
+ base::StatsScope<base::StatsRate> scope(plugin_paint);
+
+ // Plugin invalidates trigger asynchronous paints with the original
+ // invalidation rect; the plugin may be resized before the paint is handled,
+ // so we need to ensure that the damage rect is still sane.
+ const gfx::Rect paint_rect(damage_rect.Intersect(
+ gfx::Rect(0, 0, window_rect_.width(), window_rect_.height())));
+
+ ScopedActiveDelegate active_delegate(this);
+
+#ifndef NP_NO_QUICKDRAW
+ if (instance()->drawing_model() == NPDrawingModelQuickDraw)
+ qd_manager_->MakePortCurrent();
+#endif
+
+ CGContextSaveGState(context);
+
+ switch (instance()->event_model()) {
+#ifndef NP_NO_CARBON
+ case NPEventModelCarbon: {
+ NPEvent paint_event = { 0 };
+ paint_event.what = updateEvt;
+ paint_event.message = reinterpret_cast<uint32>(np_cg_context_.window);
+ paint_event.when = TickCount();
+ instance()->NPP_HandleEvent(&paint_event);
+ break;
+ }
+#endif
+ case NPEventModelCocoa: {
+ NPCocoaEvent paint_event;
+ memset(&paint_event, 0, sizeof(NPCocoaEvent));
+ paint_event.type = NPCocoaEventDrawRect;
+ paint_event.data.draw.context = context;
+ paint_event.data.draw.x = paint_rect.x();
+ paint_event.data.draw.y = paint_rect.y();
+ paint_event.data.draw.width = paint_rect.width();
+ paint_event.data.draw.height = paint_rect.height();
+ instance()->NPP_HandleEvent(&paint_event);
+ break;
+ }
+ }
+
+ // The backing buffer can change during the call to NPP_HandleEvent, in which
+ // case the old context is (or is about to be) invalid.
+ if (context == buffer_context_)
+ CGContextRestoreGState(context);
+}
+
+void WebPluginDelegateImpl::WindowlessSetWindow() {
+ if (!instance())
+ return;
+
+ window_.x = 0;
+ window_.y = 0;
+ window_.height = window_rect_.height();
+ window_.width = window_rect_.width();
+ window_.clipRect.left = clip_rect_.x();
+ window_.clipRect.top = clip_rect_.y();
+ window_.clipRect.right = clip_rect_.x() + clip_rect_.width();
+ window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height();
+
+ NPError err = instance()->NPP_SetWindow(&window_);
+
+ // Send an appropriate window focus event after the first SetWindow.
+ if (!have_called_set_window_) {
+ have_called_set_window_ = true;
+ SetWindowHasFocus(initial_window_focus_);
+ }
+
+#ifndef NP_NO_QUICKDRAW
+ if ((quirks_ & PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH) &&
+ !qd_manager_->IsFastPathEnabled() && !clip_rect_.IsEmpty()) {
+ // Give the plugin a few seconds to stabilize so we get a good initial paint
+ // to use as a baseline, then switch to the fast path.
+ fast_path_enable_tick_ = base::TimeTicks::Now() +
+ base::TimeDelta::FromSeconds(3);
+ }
+#endif
+
+ DCHECK(err == NPERR_NO_ERROR);
+}
+
+#pragma mark -
+
+bool WebPluginDelegateImpl::WindowedCreatePlugin() {
+ NOTREACHED();
+ return false;
+}
+
+void WebPluginDelegateImpl::WindowedDestroyWindow() {
+ NOTREACHED();
+}
+
+bool WebPluginDelegateImpl::WindowedReposition(const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect) {
+ NOTREACHED();
+ return false;
+}
+
+void WebPluginDelegateImpl::WindowedSetWindow() {
+ NOTREACHED();
+}
+
+#pragma mark -
+#pragma mark Mac Extensions
+
+void WebPluginDelegateImpl::PluginDidInvalidate() {
+ if (instance()->drawing_model() == NPDrawingModelInvalidatingCoreAnimation)
+ DrawLayerInSurface();
+}
+
+WebPluginDelegateImpl* WebPluginDelegateImpl::GetActiveDelegate() {
+ return g_active_delegate;
+}
+
+void WebPluginDelegateImpl::SetWindowHasFocus(bool has_focus) {
+ // If we get a window focus event before calling SetWindow, just remember the
+ // states (WindowlessSetWindow will then send it on the first call).
+ if (!have_called_set_window_) {
+ initial_window_focus_ = has_focus;
+ return;
+ }
+
+ if (has_focus == containing_window_has_focus_)
+ return;
+ containing_window_has_focus_ = has_focus;
+
+ if (!has_focus)
+ SetImeEnabled(false);
+
+#ifndef NP_NO_QUICKDRAW
+ // Make sure controls repaint with the correct look.
+ if (quirks_ & PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH)
+ SetQuickDrawFastPathEnabled(false);
+#endif
+
+ ScopedActiveDelegate active_delegate(this);
+ switch (instance()->event_model()) {
+#ifndef NP_NO_CARBON
+ case NPEventModelCarbon: {
+ NPEvent focus_event = { 0 };
+ focus_event.what = activateEvt;
+ if (has_focus)
+ focus_event.modifiers |= activeFlag;
+ focus_event.message =
+ reinterpret_cast<unsigned long>(np_cg_context_.window);
+ focus_event.when = TickCount();
+ instance()->NPP_HandleEvent(&focus_event);
+ break;
+ }
+#endif
+ case NPEventModelCocoa: {
+ NPCocoaEvent focus_event;
+ memset(&focus_event, 0, sizeof(focus_event));
+ focus_event.type = NPCocoaEventWindowFocusChanged;
+ focus_event.data.focus.hasFocus = has_focus;
+ instance()->NPP_HandleEvent(&focus_event);
+ break;
+ }
+ }
+}
+
+bool WebPluginDelegateImpl::PlatformSetPluginHasFocus(bool focused) {
+ if (!have_called_set_window_)
+ return false;
+
+ if (!focused)
+ SetImeEnabled(false);
+
+ ScopedActiveDelegate active_delegate(this);
+
+ switch (instance()->event_model()) {
+#ifndef NP_NO_CARBON
+ case NPEventModelCarbon: {
+ NPEvent focus_event = { 0 };
+ if (focused)
+ focus_event.what = NPEventType_GetFocusEvent;
+ else
+ focus_event.what = NPEventType_LoseFocusEvent;
+ focus_event.when = TickCount();
+ instance()->NPP_HandleEvent(&focus_event);
+ break;
+ }
+#endif
+ case NPEventModelCocoa: {
+ NPCocoaEvent focus_event;
+ memset(&focus_event, 0, sizeof(focus_event));
+ focus_event.type = NPCocoaEventFocusChanged;
+ focus_event.data.focus.hasFocus = focused;
+ instance()->NPP_HandleEvent(&focus_event);
+ break;
+ }
+ }
+ return true;
+}
+
+void WebPluginDelegateImpl::SetContainerVisibility(bool is_visible) {
+ if (is_visible == container_is_visible_)
+ return;
+ container_is_visible_ = is_visible;
+
+ // TODO(stuartmorgan): This is a temporary workarond for
+ // <http://crbug.com/34266>. When that is fixed, the cached_clip_rect_ code
+ // should all be removed.
+ if (is_visible) {
+ clip_rect_ = cached_clip_rect_;
+ } else {
+ clip_rect_.set_width(0);
+ clip_rect_.set_height(0);
+ }
+
+ // If the plugin is changing visibility, let the plugin know. If it's scrolled
+ // off screen (i.e., cached_clip_rect_ is empty), then container visibility
+ // doesn't change anything.
+ if (!cached_clip_rect_.IsEmpty()) {
+ PluginVisibilityChanged();
+ WindowlessSetWindow();
+ }
+
+ // When the plugin become visible, send an empty invalidate. If there were any
+ // pending invalidations this will trigger a paint event for the damaged
+ // region, and if not it's a no-op. This is necessary since higher levels
+ // that would normally do this weren't responsible for the clip_rect_ change).
+ if (!clip_rect_.IsEmpty())
+ instance()->webplugin()->InvalidateRect(gfx::Rect());
+}
+
+void WebPluginDelegateImpl::WindowFrameChanged(const gfx::Rect& window_frame,
+ const gfx::Rect& view_frame) {
+ instance()->set_window_frame(window_frame);
+ SetContentAreaOrigin(gfx::Point(view_frame.x(), view_frame.y()));
+}
+
+void WebPluginDelegateImpl::ImeCompositionConfirmed(const string16& text) {
+ if (instance()->event_model() != NPEventModelCocoa) {
+ DLOG(ERROR) << "IME text receieved in Carbon event model";
+ return;
+ }
+
+ NPCocoaEvent text_event;
+ memset(&text_event, 0, sizeof(NPCocoaEvent));
+ text_event.type = NPCocoaEventTextInput;
+ text_event.data.text.text =
+ reinterpret_cast<NPNSString*>(base::SysUTF16ToNSString(text));
+ instance()->NPP_HandleEvent(&text_event);
+}
+
+void WebPluginDelegateImpl::SetThemeCursor(ThemeCursor cursor) {
+ current_windowless_cursor_.InitFromThemeCursor(cursor);
+}
+
+void WebPluginDelegateImpl::SetCursor(const Cursor* cursor) {
+ current_windowless_cursor_.InitFromCursor(cursor);
+}
+
+void WebPluginDelegateImpl::SetNSCursor(NSCursor* cursor) {
+ current_windowless_cursor_.InitFromNSCursor(cursor);
+}
+
+#pragma mark -
+#pragma mark Internal Tracking
+
+void WebPluginDelegateImpl::SetPluginRect(const gfx::Rect& rect) {
+ bool plugin_size_changed = rect.width() != window_rect_.width() ||
+ rect.height() != window_rect_.height();
+ window_rect_ = rect;
+ PluginScreenLocationChanged();
+ if (plugin_size_changed)
+ UpdateAcceleratedSurface();
+}
+
+void WebPluginDelegateImpl::SetContentAreaOrigin(const gfx::Point& origin) {
+ content_area_origin_ = origin;
+ PluginScreenLocationChanged();
+}
+
+void WebPluginDelegateImpl::PluginScreenLocationChanged() {
+ gfx::Point plugin_origin(content_area_origin_.x() + window_rect_.x(),
+ content_area_origin_.y() + window_rect_.y());
+ instance()->set_plugin_origin(plugin_origin);
+
+#ifndef NP_NO_CARBON
+ if (instance()->event_model() == NPEventModelCarbon) {
+ UpdateDummyWindowBounds(plugin_origin);
+ }
+#endif
+}
+
+void WebPluginDelegateImpl::PluginVisibilityChanged() {
+#ifndef NP_NO_CARBON
+ if (instance()->event_model() == NPEventModelCarbon)
+ UpdateIdleEventRate();
+#endif
+ if (instance()->drawing_model() == NPDrawingModelCoreAnimation) {
+ bool plugin_visible = container_is_visible_ && !clip_rect_.IsEmpty();
+ if (plugin_visible && !redraw_timer_->IsRunning() && windowed_handle()) {
+ redraw_timer_->Start(
+ base::TimeDelta::FromMilliseconds(kCoreAnimationRedrawPeriodMs),
+ this, &WebPluginDelegateImpl::DrawLayerInSurface);
+ } else if (!plugin_visible) {
+ redraw_timer_->Stop();
+ }
+ }
+}
+
+void WebPluginDelegateImpl::SetImeEnabled(bool enabled) {
+ if (instance()->event_model() != NPEventModelCocoa)
+ return;
+ if (enabled == ime_enabled_)
+ return;
+ ime_enabled_ = enabled;
+ plugin_->SetImeEnabled(enabled);
+}
+
+#pragma mark -
+#pragma mark Core Animation Support
+
+void WebPluginDelegateImpl::DrawLayerInSurface() {
+ // If we haven't plumbed up the surface yet, don't try to draw.
+ if (!windowed_handle() || !renderer_)
+ return;
+
+ [renderer_ beginFrameAtTime:CACurrentMediaTime() timeStamp:NULL];
+ if (CGRectIsEmpty([renderer_ updateBounds])) {
+ // If nothing has changed, we are done.
+ [renderer_ endFrame];
+ return;
+ }
+
+ surface_->StartDrawing();
+
+ CGRect layerRect = [layer_ bounds];
+ [renderer_ addUpdateRect:layerRect];
+ [renderer_ render];
+ [renderer_ endFrame];
+
+ surface_->EndDrawing();
+}
+
+// Update the size of the surface to match the current size of the plug-in.
+void WebPluginDelegateImpl::UpdateAcceleratedSurface() {
+ // Will only have a window handle when using a Core Animation drawing model.
+ if (!windowed_handle() || !layer_)
+ return;
+
+ [CATransaction begin];
+ [CATransaction setValue:[NSNumber numberWithInt:0]
+ forKey:kCATransactionAnimationDuration];
+ [layer_ setFrame:CGRectMake(0, 0,
+ window_rect_.width(), window_rect_.height())];
+ [CATransaction commit];
+
+ [renderer_ setBounds:[layer_ bounds]];
+ surface_->SetSize(window_rect_.size());
+}
+
+void WebPluginDelegateImpl::set_windowed_handle(
+ gfx::PluginWindowHandle handle) {
+ windowed_handle_ = handle;
+ surface_->SetWindowHandle(handle);
+ UpdateAcceleratedSurface();
+ // Kick off the drawing timer, if necessary.
+ PluginVisibilityChanged();
+}
+
+#pragma mark -
+#pragma mark Carbon Event support
+
+#ifndef NP_NO_CARBON
+void WebPluginDelegateImpl::UpdateDummyWindowBounds(
+ const gfx::Point& plugin_origin) {
+ WindowRef window = reinterpret_cast<WindowRef>(np_cg_context_.window);
+ Rect current_bounds;
+ GetWindowBounds(window, kWindowContentRgn, &current_bounds);
+
+ Rect new_bounds;
+ // We never want to resize the window to 0x0, so if the plugin is 0x0 just
+ // move the window without resizing it.
+ if (window_rect_.width() > 0 && window_rect_.height() > 0) {
+ SetRect(&new_bounds, 0, 0, window_rect_.width(), window_rect_.height());
+ OffsetRect(&new_bounds, plugin_origin.x(), plugin_origin.y());
+ } else {
+ new_bounds = current_bounds;
+ OffsetRect(&new_bounds, plugin_origin.x() - current_bounds.left,
+ plugin_origin.y() - current_bounds.top);
+ }
+
+ if (new_bounds.left != current_bounds.left ||
+ new_bounds.top != current_bounds.top ||
+ new_bounds.right != current_bounds.right ||
+ new_bounds.bottom != current_bounds.bottom)
+ SetWindowBounds(window, kWindowContentRgn, &new_bounds);
+}
+
+void WebPluginDelegateImpl::UpdateIdleEventRate() {
+ bool plugin_visible = container_is_visible_ && !clip_rect_.IsEmpty();
+ CarbonIdleEventSource::SharedInstance()->RegisterDelegate(this,
+ plugin_visible);
+}
+
+void WebPluginDelegateImpl::FireIdleEvent() {
+ // Avoid a race condition between IO and UI threads during plugin shutdown
+ if (!instance())
+ return;
+ // Don't send idle events until we've called SetWindow.
+ if (!have_called_set_window_)
+ return;
+
+#ifndef NP_NO_QUICKDRAW
+ // Check whether it's time to turn the QuickDraw fast path back on.
+ if (!fast_path_enable_tick_.is_null() &&
+ (base::TimeTicks::Now() > fast_path_enable_tick_)) {
+ SetQuickDrawFastPathEnabled(true);
+ fast_path_enable_tick_ = base::TimeTicks();
+ }
+#endif
+
+ ScopedActiveDelegate active_delegate(this);
+
+#ifndef NP_NO_QUICKDRAW
+ if (instance()->drawing_model() == NPDrawingModelQuickDraw)
+ qd_manager_->MakePortCurrent();
+#endif
+
+ // Send an idle event so that the plugin can do background work
+ NPEvent np_event = {0};
+ np_event.what = nullEvent;
+ np_event.when = TickCount();
+ np_event.modifiers = GetCurrentKeyModifiers();
+ if (!Button())
+ np_event.modifiers |= btnState;
+ HIPoint mouse_location;
+ HIGetMousePosition(kHICoordSpaceScreenPixel, NULL, &mouse_location);
+ np_event.where.h = mouse_location.x;
+ np_event.where.v = mouse_location.y;
+ instance()->NPP_HandleEvent(&np_event);
+
+#ifndef NP_NO_QUICKDRAW
+ // Quickdraw-based plugins can draw at any time, so tell the renderer to
+ // repaint.
+ if (instance() && instance()->drawing_model() == NPDrawingModelQuickDraw)
+ instance()->webplugin()->Invalidate();
+#endif
+}
+#endif // !NP_NO_CARBON
+
+#pragma mark -
+#pragma mark QuickDraw Support
+
+#ifndef NP_NO_QUICKDRAW
+void WebPluginDelegateImpl::SetQuickDrawFastPathEnabled(bool enabled) {
+ if (!enabled) {
+ // Wait a couple of seconds, then turn the fast path back on. If we're
+ // turning it off for event handling, that ensures that the common case of
+ // move-mouse-then-click works (as well as making it likely that a second
+ // click attempt will work if the first one fails). If we're turning it
+ // off to force a new baseline image, this leaves plenty of time for the
+ // plugin to draw.
+ fast_path_enable_tick_ = base::TimeTicks::Now() +
+ base::TimeDelta::FromSeconds(2);
+ }
+
+ if (enabled == qd_manager_->IsFastPathEnabled())
+ return;
+ if (enabled && clip_rect_.IsEmpty()) {
+ // Don't switch to the fast path while the plugin is completely clipped;
+ // we can only switch when the window has an up-to-date image for us to
+ // scrape. We'll automatically switch after we become visible again.
+ return;
+ }
+
+ qd_manager_->SetFastPathEnabled(enabled);
+ qd_port_.port = qd_manager_->port();
+ WindowlessSetWindow();
+ // Send a paint event so that the new buffer gets updated immediately.
+ WindowlessPaint(buffer_context_, clip_rect_);
+}
+#endif // !NP_NO_QUICKDRAW
diff --git a/webkit/glue/plugins/webplugin_delegate_impl_win.cc b/webkit/glue/plugins/webplugin_delegate_impl_win.cc
new file mode 100644
index 0000000..e1acba1
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_delegate_impl_win.cc
@@ -0,0 +1,1410 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webplugin_delegate_impl.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "app/win/iat_patch_function.h"
+#include "base/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/message_loop.h"
+#include "base/metrics/stats_counters.h"
+#include "base/scoped_ptr.h"
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "base/win/registry.h"
+#include "base/win/windows_version.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
+#include "webkit/glue/plugins/default_plugin_shared.h"
+#include "webkit/glue/plugins/plugin_constants_win.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/glue/plugins/plugin_stream_url.h"
+#include "webkit/glue/plugins/webplugin.h"
+#include "webkit/glue/webkit_glue.h"
+
+using WebKit::WebCursorInfo;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebInputEvent;
+using WebKit::WebMouseEvent;
+
+namespace {
+
+const wchar_t kWebPluginDelegateProperty[] = L"WebPluginDelegateProperty";
+const wchar_t kPluginNameAtomProperty[] = L"PluginNameAtom";
+const wchar_t kDummyActivationWindowName[] = L"DummyWindowForActivation";
+const wchar_t kPluginFlashThrottle[] = L"FlashThrottle";
+
+// The fastest we are willing to process WM_USER+1 events for Flash.
+// Flash can easily exceed the limits of our CPU if we don't throttle it.
+// The throttle has been chosen by testing various delays and compromising
+// on acceptable Flash performance and reasonable CPU consumption.
+//
+// I'd like to make the throttle delay variable, based on the amount of
+// time currently required to paint Flash plugins. There isn't a good
+// way to count the time spent in aggregate plugin painting, however, so
+// this seems to work well enough.
+const int kFlashWMUSERMessageThrottleDelayMs = 5;
+
+// Flash displays popups in response to user clicks by posting a WM_USER
+// message to the plugin window. The handler for this message displays
+// the popup. To ensure that the popups allowed state is sent correctly
+// to the renderer we reset the popups allowed state in a timer.
+const int kWindowedPluginPopupTimerMs = 50;
+
+// The current instance of the plugin which entered the modal loop.
+WebPluginDelegateImpl* g_current_plugin_instance = NULL;
+
+typedef std::deque<MSG> ThrottleQueue;
+base::LazyInstance<ThrottleQueue> g_throttle_queue(base::LINKER_INITIALIZED);
+base::LazyInstance<std::map<HWND, WNDPROC> > g_window_handle_proc_map(
+ base::LINKER_INITIALIZED);
+
+
+// Helper object for patching the TrackPopupMenu API.
+base::LazyInstance<app::win::IATPatchFunction> g_iat_patch_track_popup_menu(
+ base::LINKER_INITIALIZED);
+
+// Helper object for patching the SetCursor API.
+base::LazyInstance<app::win::IATPatchFunction> g_iat_patch_set_cursor(
+ base::LINKER_INITIALIZED);
+
+// Helper object for patching the RegEnumKeyExW API.
+base::LazyInstance<app::win::IATPatchFunction> g_iat_patch_reg_enum_key_ex_w(
+ base::LINKER_INITIALIZED);
+
+// http://crbug.com/16114
+// Enforces providing a valid device context in NPWindow, so that NPP_SetWindow
+// is never called with NPNWindoTypeDrawable and NPWindow set to NULL.
+// Doing so allows removing NPP_SetWindow call during painting a windowless
+// plugin, which otherwise could trigger layout change while painting by
+// invoking NPN_Evaluate. Which would cause bad, bad crashes. Bad crashes.
+// TODO(dglazkov): If this approach doesn't produce regressions, move class to
+// webplugin_delegate_impl.h and implement for other platforms.
+class DrawableContextEnforcer {
+ public:
+ explicit DrawableContextEnforcer(NPWindow* window)
+ : window_(window),
+ disposable_dc_(window && !window->window) {
+ // If NPWindow is NULL, create a device context with monochrome 1x1 surface
+ // and stuff it to NPWindow.
+ if (disposable_dc_)
+ window_->window = CreateCompatibleDC(NULL);
+ }
+
+ ~DrawableContextEnforcer() {
+ if (!disposable_dc_)
+ return;
+
+ DeleteDC(static_cast<HDC>(window_->window));
+ window_->window = NULL;
+ }
+
+ private:
+ NPWindow* window_;
+ bool disposable_dc_;
+};
+
+// These are from ntddk.h
+typedef LONG NTSTATUS;
+
+#ifndef STATUS_SUCCESS
+#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
+#endif
+
+#ifndef STATUS_BUFFER_TOO_SMALL
+#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
+#endif
+
+typedef enum _KEY_INFORMATION_CLASS {
+ KeyBasicInformation,
+ KeyNodeInformation,
+ KeyFullInformation,
+ KeyNameInformation,
+ KeyCachedInformation,
+ KeyVirtualizationInformation
+} KEY_INFORMATION_CLASS;
+
+typedef struct _KEY_NAME_INFORMATION {
+ ULONG NameLength;
+ WCHAR Name[1];
+} KEY_NAME_INFORMATION, *PKEY_NAME_INFORMATION;
+
+typedef DWORD (__stdcall *ZwQueryKeyType)(
+ HANDLE key_handle,
+ int key_information_class,
+ PVOID key_information,
+ ULONG length,
+ PULONG result_length);
+
+// Returns a key's full path.
+std::wstring GetKeyPath(HKEY key) {
+ if (key == NULL)
+ return L"";
+
+ HMODULE dll = GetModuleHandle(L"ntdll.dll");
+ if (dll == NULL)
+ return L"";
+
+ ZwQueryKeyType func = reinterpret_cast<ZwQueryKeyType>(
+ ::GetProcAddress(dll, "ZwQueryKey"));
+ if (func == NULL)
+ return L"";
+
+ DWORD size = 0;
+ DWORD result = 0;
+ result = func(key, KeyNameInformation, 0, 0, &size);
+ if (result != STATUS_BUFFER_TOO_SMALL)
+ return L"";
+
+ scoped_array<char> buffer(new char[size]);
+ if (buffer.get() == NULL)
+ return L"";
+
+ result = func(key, KeyNameInformation, buffer.get(), size, &size);
+ if (result != STATUS_SUCCESS)
+ return L"";
+
+ KEY_NAME_INFORMATION* info =
+ reinterpret_cast<KEY_NAME_INFORMATION*>(buffer.get());
+ return std::wstring(info->Name, info->NameLength / sizeof(wchar_t));
+}
+
+} // namespace
+
+bool WebPluginDelegateImpl::IsPluginDelegateWindow(HWND window) {
+ // We use a buffer that is one char longer than we need to detect cases where
+ // kNativeWindowClassName is a prefix of the given window's class name. It
+ // happens that GetClassNameW will just silently truncate the class name to
+ // fit into the given buffer.
+ wchar_t class_name[arraysize(kNativeWindowClassName) + 1];
+ if (!GetClassNameW(window, class_name, arraysize(class_name)))
+ return false;
+ return wcscmp(class_name, kNativeWindowClassName) == 0;
+}
+
+bool WebPluginDelegateImpl::GetPluginNameFromWindow(
+ HWND window, std::wstring *plugin_name) {
+ if (NULL == plugin_name) {
+ return false;
+ }
+ if (!IsPluginDelegateWindow(window)) {
+ return false;
+ }
+ ATOM plugin_name_atom = reinterpret_cast<ATOM>(
+ GetPropW(window, kPluginNameAtomProperty));
+ if (plugin_name_atom != 0) {
+ WCHAR plugin_name_local[MAX_PATH] = {0};
+ GlobalGetAtomNameW(plugin_name_atom,
+ plugin_name_local,
+ ARRAYSIZE(plugin_name_local));
+ *plugin_name = plugin_name_local;
+ return true;
+ }
+ return false;
+}
+
+bool WebPluginDelegateImpl::IsDummyActivationWindow(HWND window) {
+ if (!IsWindow(window))
+ return false;
+
+ wchar_t window_title[MAX_PATH + 1] = {0};
+ if (GetWindowText(window, window_title, arraysize(window_title))) {
+ return (0 == lstrcmpiW(window_title, kDummyActivationWindowName));
+ }
+ return false;
+}
+
+LRESULT CALLBACK WebPluginDelegateImpl::HandleEventMessageFilterHook(
+ int code, WPARAM wParam, LPARAM lParam) {
+ if (g_current_plugin_instance) {
+ g_current_plugin_instance->OnModalLoopEntered();
+ } else {
+ NOTREACHED();
+ }
+ return CallNextHookEx(NULL, code, wParam, lParam);
+}
+
+LRESULT CALLBACK WebPluginDelegateImpl::MouseHookProc(
+ int code, WPARAM wParam, LPARAM lParam) {
+ if (code == HC_ACTION) {
+ MOUSEHOOKSTRUCT* hook_struct = reinterpret_cast<MOUSEHOOKSTRUCT*>(lParam);
+ if (hook_struct)
+ HandleCaptureForMessage(hook_struct->hwnd, wParam);
+ }
+
+ return CallNextHookEx(NULL, code, wParam, lParam);
+}
+
+WebPluginDelegateImpl::WebPluginDelegateImpl(
+ gfx::PluginWindowHandle containing_view,
+ NPAPI::PluginInstance *instance)
+ : parent_(containing_view),
+ instance_(instance),
+ quirks_(0),
+ plugin_(NULL),
+ windowless_(false),
+ windowed_handle_(NULL),
+ windowed_did_set_window_(false),
+ plugin_wnd_proc_(NULL),
+ last_message_(0),
+ is_calling_wndproc(false),
+ keyboard_layout_(NULL),
+ parent_thread_id_(0),
+ dummy_window_for_activation_(NULL),
+ handle_event_message_filter_hook_(NULL),
+ handle_event_pump_messages_event_(NULL),
+ user_gesture_message_posted_(false),
+#pragma warning(suppress: 4355) // can use this
+ user_gesture_msg_factory_(this),
+ handle_event_depth_(0),
+ mouse_hook_(NULL),
+ first_set_window_call_(true),
+ plugin_has_focus_(false),
+ has_webkit_focus_(false),
+ containing_view_has_focus_(true),
+ creation_succeeded_(false) {
+ memset(&window_, 0, sizeof(window_));
+
+ const WebPluginInfo& plugin_info = instance_->plugin_lib()->plugin_info();
+ std::wstring filename =
+ StringToLowerASCII(plugin_info.path.BaseName().value());
+
+ if (instance_->mime_type() == "application/x-shockwave-flash" ||
+ filename == kFlashPlugin) {
+ // Flash only requests windowless plugins if we return a Mozilla user
+ // agent.
+ instance_->set_use_mozilla_user_agent();
+ quirks_ |= PLUGIN_QUIRK_THROTTLE_WM_USER_PLUS_ONE;
+ quirks_ |= PLUGIN_QUIRK_PATCH_SETCURSOR;
+ quirks_ |= PLUGIN_QUIRK_ALWAYS_NOTIFY_SUCCESS;
+ quirks_ |= PLUGIN_QUIRK_HANDLE_MOUSE_CAPTURE;
+ } else if (filename == kAcrobatReaderPlugin) {
+ // Check for the version number above or equal 9.
+ std::vector<std::wstring> version;
+ base::SplitString(plugin_info.version, L'.', &version);
+ if (version.size() > 0) {
+ int major;
+ base::StringToInt(version[0], &major);
+ if (major >= 9) {
+ quirks_ |= PLUGIN_QUIRK_DIE_AFTER_UNLOAD;
+
+ // 9.2 needs this.
+ quirks_ |= PLUGIN_QUIRK_SETWINDOW_TWICE;
+ }
+ }
+ quirks_ |= PLUGIN_QUIRK_BLOCK_NONSTANDARD_GETURL_REQUESTS;
+ } else if (plugin_info.name.find(L"Windows Media Player") !=
+ std::wstring::npos) {
+ // Windows Media Player needs two NPP_SetWindow calls.
+ quirks_ |= PLUGIN_QUIRK_SETWINDOW_TWICE;
+
+ // Windowless mode doesn't work in the WMP NPAPI plugin.
+ quirks_ |= PLUGIN_QUIRK_NO_WINDOWLESS;
+
+ // The media player plugin sets its size on the first NPP_SetWindow call
+ // and never updates its size. We should call the underlying NPP_SetWindow
+ // only when we have the correct size.
+ quirks_ |= PLUGIN_QUIRK_IGNORE_FIRST_SETWINDOW_CALL;
+
+ if (filename == kOldWMPPlugin) {
+ // Non-admin users on XP couldn't modify the key to force the new UI.
+ quirks_ |= PLUGIN_QUIRK_PATCH_REGENUMKEYEXW;
+ }
+ } else if (instance_->mime_type() == "audio/x-pn-realaudio-plugin" ||
+ filename == kRealPlayerPlugin) {
+ quirks_ |= PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY;
+ } else if (plugin_info.name.find(L"VLC Multimedia Plugin") !=
+ std::wstring::npos ||
+ plugin_info.name.find(L"VLC Multimedia Plug-in") !=
+ std::wstring::npos) {
+ // VLC hangs on NPP_Destroy if we call NPP_SetWindow with a null window
+ // handle
+ quirks_ |= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY;
+ // VLC 0.8.6d and 0.8.6e crash if multiple instances are created.
+ quirks_ |= PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES;
+ } else if (filename == kSilverlightPlugin) {
+ // Explanation for this quirk can be found in
+ // WebPluginDelegateImpl::Initialize.
+ quirks_ |= PLUGIN_QUIRK_PATCH_SETCURSOR;
+ } else if (plugin_info.name.find(L"DivX Web Player") !=
+ std::wstring::npos) {
+ // The divx plugin sets its size on the first NPP_SetWindow call and never
+ // updates its size. We should call the underlying NPP_SetWindow only when
+ // we have the correct size.
+ quirks_ |= PLUGIN_QUIRK_IGNORE_FIRST_SETWINDOW_CALL;
+ }
+}
+
+WebPluginDelegateImpl::~WebPluginDelegateImpl() {
+ if (::IsWindow(dummy_window_for_activation_)) {
+ ::DestroyWindow(dummy_window_for_activation_);
+ }
+
+ DestroyInstance();
+
+ if (!windowless_)
+ WindowedDestroyWindow();
+
+ if (handle_event_pump_messages_event_) {
+ CloseHandle(handle_event_pump_messages_event_);
+ }
+}
+
+bool WebPluginDelegateImpl::PlatformInitialize() {
+ plugin_->SetWindow(windowed_handle_);
+
+ if (windowless_ && !instance_->plugin_lib()->internal()) {
+ CreateDummyWindowForActivation();
+ handle_event_pump_messages_event_ = CreateEvent(NULL, TRUE, FALSE, NULL);
+ plugin_->SetWindowlessPumpEvent(handle_event_pump_messages_event_);
+ }
+
+ // We cannot patch internal plugins as they are not shared libraries.
+ if (!instance_->plugin_lib()->internal()) {
+ // Windowless plugins call the WindowFromPoint API and passes the result of
+ // that to the TrackPopupMenu API call as the owner window. This causes the
+ // API to fail as the API expects the window handle to live on the same
+ // thread as the caller. It works in the other browsers as the plugin lives
+ // on the browser thread. Our workaround is to intercept the TrackPopupMenu
+ // API and replace the window handle with the dummy activation window.
+ if (windowless_ && !g_iat_patch_track_popup_menu.Pointer()->is_patched()) {
+ g_iat_patch_track_popup_menu.Pointer()->Patch(
+ GetPluginPath().value().c_str(), "user32.dll", "TrackPopupMenu",
+ WebPluginDelegateImpl::TrackPopupMenuPatch);
+ }
+
+ // Windowless plugins can set cursors by calling the SetCursor API. This
+ // works because the thread inputs of the browser UI thread and the plugin
+ // thread are attached. We intercept the SetCursor API for windowless
+ // plugins and remember the cursor being set. This is shipped over to the
+ // browser in the HandleEvent call, which ensures that the cursor does not
+ // change when a windowless plugin instance changes the cursor
+ // in a background tab.
+ if (windowless_ && !g_iat_patch_set_cursor.Pointer()->is_patched() &&
+ (quirks_ & PLUGIN_QUIRK_PATCH_SETCURSOR)) {
+ g_iat_patch_set_cursor.Pointer()->Patch(
+ GetPluginPath().value().c_str(), "user32.dll", "SetCursor",
+ WebPluginDelegateImpl::SetCursorPatch);
+ }
+
+ // The windowed flash plugin has a bug which occurs when the plugin enters
+ // fullscreen mode. It basically captures the mouse on WM_LBUTTONDOWN and
+ // does not release capture correctly causing it to stop receiving
+ // subsequent mouse events. This problem is also seen in Safari where there
+ // is code to handle this in the wndproc. However the plugin subclasses the
+ // window again in WM_LBUTTONDOWN before entering full screen. As a result
+ // Safari does not receive the WM_LBUTTONUP message. To workaround this
+ // issue we use a per thread mouse hook. This bug does not occur in Firefox
+ // and opera. Firefox has code similar to Safari. It could well be a bug in
+ // the flash plugin, which only occurs in webkit based browsers.
+ if (quirks_ & PLUGIN_QUIRK_HANDLE_MOUSE_CAPTURE) {
+ mouse_hook_ = SetWindowsHookEx(WH_MOUSE, MouseHookProc, NULL,
+ GetCurrentThreadId());
+ }
+ }
+
+ // On XP, WMP will use its old UI unless a registry key under HKLM has the
+ // name of the current process. We do it in the installer for admin users,
+ // for the rest patch this function.
+ if ((quirks_ & PLUGIN_QUIRK_PATCH_REGENUMKEYEXW) &&
+ base::win::GetVersion() == base::win::VERSION_XP &&
+ !base::win::RegKey().Open(HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Microsoft\\MediaPlayer\\ShimInclusionList\\chrome.exe",
+ KEY_READ) &&
+ !g_iat_patch_reg_enum_key_ex_w.Pointer()->is_patched()) {
+ g_iat_patch_reg_enum_key_ex_w.Pointer()->Patch(
+ L"wmpdxm.dll", "advapi32.dll", "RegEnumKeyExW",
+ WebPluginDelegateImpl::RegEnumKeyExWPatch);
+ }
+
+ return true;
+}
+
+void WebPluginDelegateImpl::PlatformDestroyInstance() {
+ if (!instance_->plugin_lib())
+ return;
+
+ // Unpatch if this is the last plugin instance.
+ if (instance_->plugin_lib()->instance_count() != 1)
+ return;
+
+ if (g_iat_patch_set_cursor.Pointer()->is_patched())
+ g_iat_patch_set_cursor.Pointer()->Unpatch();
+
+ if (g_iat_patch_track_popup_menu.Pointer()->is_patched())
+ g_iat_patch_track_popup_menu.Pointer()->Unpatch();
+
+ if (g_iat_patch_reg_enum_key_ex_w.Pointer()->is_patched())
+ g_iat_patch_reg_enum_key_ex_w.Pointer()->Unpatch();
+
+ if (mouse_hook_) {
+ UnhookWindowsHookEx(mouse_hook_);
+ mouse_hook_ = NULL;
+ }
+}
+
+void WebPluginDelegateImpl::Paint(skia::PlatformCanvas* canvas,
+ const gfx::Rect& rect) {
+ if (windowless_) {
+ HDC hdc = canvas->beginPlatformPaint();
+ WindowlessPaint(hdc, rect);
+ canvas->endPlatformPaint();
+ }
+}
+
+void WebPluginDelegateImpl::Print(HDC hdc) {
+ // Disabling the call to NPP_Print as it causes a crash in
+ // flash in some cases. In any case this does not work as expected
+ // as the EMF meta file dc passed in needs to be created with the
+ // the plugin window dc as its sibling dc and the window rect
+ // in .01 mm units.
+#if 0
+ NPPrint npprint;
+ npprint.mode = NP_EMBED;
+ npprint.print.embedPrint.platformPrint = reinterpret_cast<void*>(hdc);
+ npprint.print.embedPrint.window = window_;
+ instance()->NPP_Print(&npprint);
+#endif
+}
+
+void WebPluginDelegateImpl::InstallMissingPlugin() {
+ NPEvent evt;
+ evt.event = default_plugin::kInstallMissingPluginMessage;
+ evt.lParam = 0;
+ evt.wParam = 0;
+ instance()->NPP_HandleEvent(&evt);
+}
+
+bool WebPluginDelegateImpl::WindowedCreatePlugin() {
+ DCHECK(!windowed_handle_);
+
+ RegisterNativeWindowClass();
+
+ // The window will be sized and shown later.
+ windowed_handle_ = CreateWindowEx(
+ WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR,
+ kNativeWindowClassName,
+ 0,
+ WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
+ 0,
+ 0,
+ 0,
+ 0,
+ parent_,
+ 0,
+ GetModuleHandle(NULL),
+ 0);
+ if (windowed_handle_ == 0)
+ return false;
+
+ if (IsWindow(parent_)) {
+ // This is a tricky workaround for Issue 2673 in chromium "Flash: IME not
+ // available". To use IMEs in this window, we have to make Windows attach
+ // IMEs to this window (i.e. load IME DLLs, attach them to this process,
+ // and add their message hooks to this window). Windows attaches IMEs while
+ // this process creates a top-level window. On the other hand, to layout
+ // this window correctly in the given parent window (RenderWidgetHostHWND),
+ // this window should be a child window of the parent window.
+ // To satisfy both of the above conditions, this code once creates a
+ // top-level window and change it to a child window of the parent window.
+ SetWindowLongPtr(windowed_handle_, GWL_STYLE,
+ WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
+ SetParent(windowed_handle_, parent_);
+ }
+
+ BOOL result = SetProp(windowed_handle_, kWebPluginDelegateProperty, this);
+ DCHECK(result == TRUE) << "SetProp failed, last error = " << GetLastError();
+ // Get the name of the plugin, create an atom and set that in a window
+ // property. Use an atom so that other processes can access the name of
+ // the plugin that this window is hosting
+ if (instance_ != NULL) {
+ NPAPI::PluginLib* plugin_lib = instance()->plugin_lib();
+ if (plugin_lib != NULL) {
+ std::wstring plugin_name = plugin_lib->plugin_info().name;
+ if (!plugin_name.empty()) {
+ ATOM plugin_name_atom = GlobalAddAtomW(plugin_name.c_str());
+ DCHECK(0 != plugin_name_atom);
+ result = SetProp(windowed_handle_,
+ kPluginNameAtomProperty,
+ reinterpret_cast<HANDLE>(plugin_name_atom));
+ DCHECK(result == TRUE) << "SetProp failed, last error = " <<
+ GetLastError();
+ }
+ }
+ }
+
+ // Calling SetWindowLongPtrA here makes the window proc ASCII, which is
+ // required by at least the Shockwave Director plug-in.
+ SetWindowLongPtrA(
+ windowed_handle_, GWL_WNDPROC, reinterpret_cast<LONG>(DefWindowProcA));
+
+ return true;
+}
+
+void WebPluginDelegateImpl::WindowedDestroyWindow() {
+ if (windowed_handle_ != NULL) {
+ // Unsubclass the window.
+ WNDPROC current_wnd_proc = reinterpret_cast<WNDPROC>(
+ GetWindowLongPtr(windowed_handle_, GWLP_WNDPROC));
+ if (current_wnd_proc == NativeWndProc) {
+ SetWindowLongPtr(windowed_handle_,
+ GWLP_WNDPROC,
+ reinterpret_cast<LONG>(plugin_wnd_proc_));
+ }
+
+ plugin_->WillDestroyWindow(windowed_handle_);
+
+ DestroyWindow(windowed_handle_);
+ windowed_handle_ = 0;
+ }
+}
+
+// Erase all messages in the queue destined for a particular window.
+// When windows are closing, callers should use this function to clear
+// the queue.
+// static
+void WebPluginDelegateImpl::ClearThrottleQueueForWindow(HWND window) {
+ ThrottleQueue* throttle_queue = g_throttle_queue.Pointer();
+
+ ThrottleQueue::iterator it;
+ for (it = throttle_queue->begin(); it != throttle_queue->end(); ) {
+ if (it->hwnd == window) {
+ it = throttle_queue->erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
+// Delayed callback for processing throttled messages.
+// Throttled messages are aggregated globally across all plugins.
+// static
+void WebPluginDelegateImpl::OnThrottleMessage() {
+ // The current algorithm walks the list and processes the first
+ // message it finds for each plugin. It is important to service
+ // all active plugins with each pass through the throttle, otherwise
+ // we see video jankiness. Copy the set to notify before notifying
+ // since we may re-enter OnThrottleMessage from CallWindowProc!
+ ThrottleQueue* throttle_queue = g_throttle_queue.Pointer();
+ ThrottleQueue notify_queue;
+ std::set<HWND> processed;
+
+ ThrottleQueue::iterator it = throttle_queue->begin();
+ while (it != throttle_queue->end()) {
+ const MSG& msg = *it;
+ if (processed.find(msg.hwnd) == processed.end()) {
+ processed.insert(msg.hwnd);
+ notify_queue.push_back(msg);
+ it = throttle_queue->erase(it);
+ } else {
+ it++;
+ }
+ }
+
+ for (it = notify_queue.begin(); it != notify_queue.end(); ++it) {
+ const MSG& msg = *it;
+ WNDPROC proc = reinterpret_cast<WNDPROC>(msg.time);
+ // It is possible that the window was closed after we queued
+ // this message. This is a rare event; just verify the window
+ // is alive. (see also bug 1259488)
+ if (IsWindow(msg.hwnd))
+ CallWindowProc(proc, msg.hwnd, msg.message, msg.wParam, msg.lParam);
+ }
+
+ if (!throttle_queue->empty()) {
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ NewRunnableFunction(&WebPluginDelegateImpl::OnThrottleMessage),
+ kFlashWMUSERMessageThrottleDelayMs);
+ }
+}
+
+// Schedule a windows message for delivery later.
+// static
+void WebPluginDelegateImpl::ThrottleMessage(WNDPROC proc, HWND hwnd,
+ UINT message, WPARAM wParam,
+ LPARAM lParam) {
+ MSG msg;
+ msg.time = reinterpret_cast<DWORD>(proc);
+ msg.hwnd = hwnd;
+ msg.message = message;
+ msg.wParam = wParam;
+ msg.lParam = lParam;
+
+ ThrottleQueue* throttle_queue = g_throttle_queue.Pointer();
+
+ throttle_queue->push_back(msg);
+
+ if (throttle_queue->size() == 1) {
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ NewRunnableFunction(&WebPluginDelegateImpl::OnThrottleMessage),
+ kFlashWMUSERMessageThrottleDelayMs);
+ }
+}
+
+// We go out of our way to find the hidden windows created by Flash for
+// windowless plugins. We throttle the rate at which they deliver messages
+// so that they will not consume outrageous amounts of CPU.
+// static
+LRESULT CALLBACK WebPluginDelegateImpl::FlashWindowlessWndProc(HWND hwnd,
+ UINT message, WPARAM wparam, LPARAM lparam) {
+ std::map<HWND, WNDPROC>::iterator index =
+ g_window_handle_proc_map.Get().find(hwnd);
+
+ WNDPROC old_proc = (*index).second;
+ DCHECK(old_proc);
+
+ switch (message) {
+ case WM_NCDESTROY: {
+ WebPluginDelegateImpl::ClearThrottleQueueForWindow(hwnd);
+ g_window_handle_proc_map.Get().erase(index);
+ break;
+ }
+ // Flash may flood the message queue with WM_USER+1 message causing 100% CPU
+ // usage. See https://bugzilla.mozilla.org/show_bug.cgi?id=132759. We
+ // prevent this by throttling the messages.
+ case WM_USER + 1: {
+ WebPluginDelegateImpl::ThrottleMessage(old_proc, hwnd, message, wparam,
+ lparam);
+ return TRUE;
+ }
+ default: {
+ break;
+ }
+ }
+ return CallWindowProc(old_proc, hwnd, message, wparam, lparam);
+}
+
+// Callback for enumerating the Flash windows.
+BOOL CALLBACK EnumFlashWindows(HWND window, LPARAM arg) {
+ WNDPROC wnd_proc = reinterpret_cast<WNDPROC>(arg);
+ TCHAR class_name[1024];
+ if (!RealGetWindowClass(window, class_name,
+ sizeof(class_name)/sizeof(TCHAR))) {
+ LOG(ERROR) << "RealGetWindowClass failure: " << GetLastError();
+ return FALSE;
+ }
+
+ if (wcscmp(class_name, L"SWFlash_PlaceholderX"))
+ return TRUE;
+
+ WNDPROC current_wnd_proc = reinterpret_cast<WNDPROC>(
+ GetWindowLongPtr(window, GWLP_WNDPROC));
+ if (current_wnd_proc != wnd_proc) {
+ WNDPROC old_flash_proc = reinterpret_cast<WNDPROC>(SetWindowLongPtr(
+ window, GWLP_WNDPROC,
+ reinterpret_cast<LONG>(wnd_proc)));
+ DCHECK(old_flash_proc);
+ g_window_handle_proc_map.Get()[window] = old_flash_proc;
+ }
+
+ return TRUE;
+}
+
+bool WebPluginDelegateImpl::CreateDummyWindowForActivation() {
+ DCHECK(!dummy_window_for_activation_);
+ dummy_window_for_activation_ = CreateWindowEx(
+ 0,
+ L"Static",
+ kDummyActivationWindowName,
+ WS_CHILD,
+ 0,
+ 0,
+ 0,
+ 0,
+ parent_,
+ 0,
+ GetModuleHandle(NULL),
+ 0);
+
+ if (dummy_window_for_activation_ == 0)
+ return false;
+
+ // Flash creates background windows which use excessive CPU in our
+ // environment; we wrap these windows and throttle them so that they don't
+ // get out of hand.
+ if (!EnumThreadWindows(::GetCurrentThreadId(), EnumFlashWindows,
+ reinterpret_cast<LPARAM>(
+ &WebPluginDelegateImpl::FlashWindowlessWndProc))) {
+ // Log that this happened. Flash will still work; it just means the
+ // throttle isn't installed (and Flash will use more CPU).
+ NOTREACHED();
+ LOG(ERROR) << "Failed to wrap all windowless Flash windows";
+ }
+ return true;
+}
+
+bool WebPluginDelegateImpl::WindowedReposition(
+ const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect) {
+ if (!windowed_handle_) {
+ NOTREACHED();
+ return false;
+ }
+
+ if (window_rect_ == window_rect && clip_rect_ == clip_rect)
+ return false;
+
+ // We only set the plugin's size here. Its position is moved elsewhere, which
+ // allows the window moves/scrolling/clipping to be synchronized with the page
+ // and other windows.
+ // If the plugin window has no parent, then don't focus it because it isn't
+ // being displayed anywhere. See:
+ // http://code.google.com/p/chromium/issues/detail?id=32658
+ if (window_rect.size() != window_rect_.size()) {
+ UINT flags = SWP_NOMOVE | SWP_NOZORDER;
+ if (!GetParent(windowed_handle_))
+ flags |= SWP_NOACTIVATE;
+ ::SetWindowPos(windowed_handle_,
+ NULL,
+ 0,
+ 0,
+ window_rect.width(),
+ window_rect.height(),
+ flags);
+ }
+
+ window_rect_ = window_rect;
+ clip_rect_ = clip_rect;
+
+ // Ensure that the entire window gets repainted.
+ ::InvalidateRect(windowed_handle_, NULL, FALSE);
+
+ return true;
+}
+
+void WebPluginDelegateImpl::WindowedSetWindow() {
+ if (!instance_)
+ return;
+
+ if (!windowed_handle_) {
+ NOTREACHED();
+ return;
+ }
+
+ instance()->set_window_handle(windowed_handle_);
+
+ DCHECK(!instance()->windowless());
+
+ window_.clipRect.top = std::max(0, clip_rect_.y());
+ window_.clipRect.left = std::max(0, clip_rect_.x());
+ window_.clipRect.bottom = std::max(0, clip_rect_.y() + clip_rect_.height());
+ window_.clipRect.right = std::max(0, clip_rect_.x() + clip_rect_.width());
+ window_.height = window_rect_.height();
+ window_.width = window_rect_.width();
+ window_.x = 0;
+ window_.y = 0;
+
+ window_.window = windowed_handle_;
+ window_.type = NPWindowTypeWindow;
+
+ // Reset this flag before entering the instance in case of side-effects.
+ windowed_did_set_window_ = true;
+
+ NPError err = instance()->NPP_SetWindow(&window_);
+ if (quirks_ & PLUGIN_QUIRK_SETWINDOW_TWICE)
+ instance()->NPP_SetWindow(&window_);
+
+ WNDPROC current_wnd_proc = reinterpret_cast<WNDPROC>(
+ GetWindowLongPtr(windowed_handle_, GWLP_WNDPROC));
+ if (current_wnd_proc != NativeWndProc) {
+ plugin_wnd_proc_ = reinterpret_cast<WNDPROC>(SetWindowLongPtr(
+ windowed_handle_, GWLP_WNDPROC, reinterpret_cast<LONG>(NativeWndProc)));
+ }
+}
+
+ATOM WebPluginDelegateImpl::RegisterNativeWindowClass() {
+ static bool have_registered_window_class = false;
+ if (have_registered_window_class == true)
+ return true;
+
+ have_registered_window_class = true;
+
+ WNDCLASSEX wcex;
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.style = CS_DBLCLKS;
+ wcex.lpfnWndProc = DummyWindowProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = GetModuleHandle(NULL);
+ wcex.hIcon = 0;
+ wcex.hCursor = 0;
+ // Some plugins like windows media player 11 create child windows parented
+ // by our plugin window, where the media content is rendered. These plugins
+ // dont implement WM_ERASEBKGND, which causes painting issues, when the
+ // window where the media is rendered is moved around. DefWindowProc does
+ // implement WM_ERASEBKGND correctly if we have a valid background brush.
+ wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1);
+ wcex.lpszMenuName = 0;
+ wcex.lpszClassName = kNativeWindowClassName;
+ wcex.hIconSm = 0;
+
+ return RegisterClassEx(&wcex);
+}
+
+LRESULT CALLBACK WebPluginDelegateImpl::DummyWindowProc(
+ HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
+ // This is another workaround for Issue 2673 in chromium "Flash: IME not
+ // available". Somehow, the CallWindowProc() function does not dispatch
+ // window messages when its first parameter is a handle representing the
+ // DefWindowProc() function. To avoid this problem, this code creates a
+ // wrapper function which just encapsulates the DefWindowProc() function
+ // and set it as the window procedure of a windowed plug-in.
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+// Returns true if the message passed in corresponds to a user gesture.
+static bool IsUserGestureMessage(unsigned int message) {
+ switch (message) {
+ case WM_LBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_KEYUP:
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+LRESULT CALLBACK WebPluginDelegateImpl::NativeWndProc(
+ HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
+ WebPluginDelegateImpl* delegate = reinterpret_cast<WebPluginDelegateImpl*>(
+ GetProp(hwnd, kWebPluginDelegateProperty));
+ if (!delegate) {
+ NOTREACHED();
+ return 0;
+ }
+
+ if (message == delegate->last_message_ &&
+ delegate->GetQuirks() & PLUGIN_QUIRK_DONT_CALL_WND_PROC_RECURSIVELY &&
+ delegate->is_calling_wndproc) {
+ // Real may go into a state where it recursively dispatches the same event
+ // when subclassed. See https://bugzilla.mozilla.org/show_bug.cgi?id=192914
+ // We only do the recursive check for Real because it's possible and valid
+ // for a plugin to synchronously dispatch a message to itself such that it
+ // looks like it's in recursion.
+ return TRUE;
+ }
+
+ // Flash may flood the message queue with WM_USER+1 message causing 100% CPU
+ // usage. See https://bugzilla.mozilla.org/show_bug.cgi?id=132759. We
+ // prevent this by throttling the messages.
+ if (message == WM_USER + 1 &&
+ delegate->GetQuirks() & PLUGIN_QUIRK_THROTTLE_WM_USER_PLUS_ONE) {
+ WebPluginDelegateImpl::ThrottleMessage(delegate->plugin_wnd_proc_, hwnd,
+ message, wparam, lparam);
+ return FALSE;
+ }
+
+ LRESULT result;
+ uint32 old_message = delegate->last_message_;
+ delegate->last_message_ = message;
+
+ static UINT custom_msg = RegisterWindowMessage(kPaintMessageName);
+ if (message == custom_msg) {
+ // Get the invalid rect which is in screen coordinates and convert to
+ // window coordinates.
+ gfx::Rect invalid_rect;
+ invalid_rect.set_x(wparam >> 16);
+ invalid_rect.set_y(wparam & 0xFFFF);
+ invalid_rect.set_width(lparam >> 16);
+ invalid_rect.set_height(lparam & 0xFFFF);
+
+ RECT window_rect;
+ GetWindowRect(hwnd, &window_rect);
+ invalid_rect.Offset(-window_rect.left, -window_rect.top);
+
+ // The plugin window might have non-client area. If we don't pass in
+ // RDW_FRAME then the children don't receive WM_NCPAINT messages while
+ // scrolling, which causes painting problems (http://b/issue?id=923945).
+ uint32 flags = RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_FRAME;
+
+ // If a plugin (like Google Earth or Java) has child windows that are hosted
+ // in a different process, then RedrawWindow with UPDATENOW will
+ // synchronously wait for this call to complete. Some messages are pumped
+ // but not others, which could lead to a deadlock. So avoid reentrancy by
+ // only synchronously calling RedrawWindow once at a time.
+ if (old_message != custom_msg)
+ flags |= RDW_UPDATENOW;
+
+ RedrawWindow(hwnd, &invalid_rect.ToRECT(), NULL, flags);
+ result = FALSE;
+ } else {
+ delegate->is_calling_wndproc = true;
+
+ if (!delegate->user_gesture_message_posted_ &&
+ IsUserGestureMessage(message)) {
+ delegate->user_gesture_message_posted_ = true;
+
+ delegate->instance()->PushPopupsEnabledState(true);
+
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ delegate->user_gesture_msg_factory_.NewRunnableMethod(
+ &WebPluginDelegateImpl::OnUserGestureEnd),
+ kWindowedPluginPopupTimerMs);
+ }
+
+ HandleCaptureForMessage(hwnd, message);
+
+ // Maintain a local/global stack for the g_current_plugin_instance variable
+ // as this may be a nested invocation.
+ WebPluginDelegateImpl* last_plugin_instance = g_current_plugin_instance;
+
+ g_current_plugin_instance = delegate;
+
+ result = CallWindowProc(
+ delegate->plugin_wnd_proc_, hwnd, message, wparam, lparam);
+
+ delegate->is_calling_wndproc = false;
+ g_current_plugin_instance = last_plugin_instance;
+
+ if (message == WM_NCDESTROY) {
+ RemoveProp(hwnd, kWebPluginDelegateProperty);
+ ATOM plugin_name_atom = reinterpret_cast<ATOM>(
+ RemoveProp(hwnd, kPluginNameAtomProperty));
+ if (plugin_name_atom != 0)
+ GlobalDeleteAtom(plugin_name_atom);
+ ClearThrottleQueueForWindow(hwnd);
+ }
+ }
+ delegate->last_message_ = old_message;
+ return result;
+}
+
+void WebPluginDelegateImpl::WindowlessUpdateGeometry(
+ const gfx::Rect& window_rect,
+ const gfx::Rect& clip_rect) {
+ bool window_rect_changed = (window_rect_ != window_rect);
+ // Only resend to the instance if the geometry has changed.
+ if (!window_rect_changed && clip_rect == clip_rect_)
+ return;
+
+ clip_rect_ = clip_rect;
+ window_rect_ = window_rect;
+
+ WindowlessSetWindow();
+
+ if (window_rect_changed) {
+ WINDOWPOS win_pos = {0};
+ win_pos.x = window_rect_.x();
+ win_pos.y = window_rect_.y();
+ win_pos.cx = window_rect_.width();
+ win_pos.cy = window_rect_.height();
+
+ NPEvent pos_changed_event;
+ pos_changed_event.event = WM_WINDOWPOSCHANGED;
+ pos_changed_event.wParam = 0;
+ pos_changed_event.lParam = PtrToUlong(&win_pos);
+
+ instance()->NPP_HandleEvent(&pos_changed_event);
+ }
+}
+
+void WebPluginDelegateImpl::WindowlessPaint(HDC hdc,
+ const gfx::Rect& damage_rect) {
+ DCHECK(hdc);
+
+ RECT damage_rect_win;
+ damage_rect_win.left = damage_rect.x(); // + window_rect_.x();
+ damage_rect_win.top = damage_rect.y(); // + window_rect_.y();
+ damage_rect_win.right = damage_rect_win.left + damage_rect.width();
+ damage_rect_win.bottom = damage_rect_win.top + damage_rect.height();
+
+ // Save away the old HDC as this could be a nested invocation.
+ void* old_dc = window_.window;
+ window_.window = hdc;
+
+ NPEvent paint_event;
+ paint_event.event = WM_PAINT;
+ // NOTE: NPAPI is not 64bit safe. It puts pointers into 32bit values.
+ paint_event.wParam = PtrToUlong(hdc);
+ paint_event.lParam = PtrToUlong(&damage_rect_win);
+ static base::StatsRate plugin_paint("Plugin.Paint");
+ base::StatsScope<base::StatsRate> scope(plugin_paint);
+ instance()->NPP_HandleEvent(&paint_event);
+ window_.window = old_dc;
+}
+
+void WebPluginDelegateImpl::WindowlessSetWindow() {
+ if (!instance())
+ return;
+
+ if (window_rect_.IsEmpty()) // wait for geometry to be set.
+ return;
+
+ DCHECK(instance()->windowless());
+
+ window_.clipRect.top = clip_rect_.y();
+ window_.clipRect.left = clip_rect_.x();
+ window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height();
+ window_.clipRect.right = clip_rect_.x() + clip_rect_.width();
+ window_.height = window_rect_.height();
+ window_.width = window_rect_.width();
+ window_.x = window_rect_.x();
+ window_.y = window_rect_.y();
+ window_.type = NPWindowTypeDrawable;
+ DrawableContextEnforcer enforcer(&window_);
+
+ NPError err = instance()->NPP_SetWindow(&window_);
+ DCHECK(err == NPERR_NO_ERROR);
+}
+
+bool WebPluginDelegateImpl::PlatformSetPluginHasFocus(bool focused) {
+ DCHECK(instance()->windowless());
+
+ NPEvent focus_event;
+ focus_event.event = focused ? WM_SETFOCUS : WM_KILLFOCUS;
+ focus_event.wParam = 0;
+ focus_event.lParam = 0;
+
+ instance()->NPP_HandleEvent(&focus_event);
+ return true;
+}
+
+static bool NPEventFromWebMouseEvent(const WebMouseEvent& event,
+ NPEvent *np_event) {
+ np_event->lParam = static_cast<uint32>(MAKELPARAM(event.windowX,
+ event.windowY));
+ np_event->wParam = 0;
+
+ if (event.modifiers & WebInputEvent::ControlKey)
+ np_event->wParam |= MK_CONTROL;
+ if (event.modifiers & WebInputEvent::ShiftKey)
+ np_event->wParam |= MK_SHIFT;
+ if (event.modifiers & WebInputEvent::LeftButtonDown)
+ np_event->wParam |= MK_LBUTTON;
+ if (event.modifiers & WebInputEvent::MiddleButtonDown)
+ np_event->wParam |= MK_MBUTTON;
+ if (event.modifiers & WebInputEvent::RightButtonDown)
+ np_event->wParam |= MK_RBUTTON;
+
+ switch (event.type) {
+ case WebInputEvent::MouseMove:
+ case WebInputEvent::MouseLeave:
+ case WebInputEvent::MouseEnter:
+ np_event->event = WM_MOUSEMOVE;
+ return true;
+ case WebInputEvent::MouseDown:
+ switch (event.button) {
+ case WebMouseEvent::ButtonLeft:
+ np_event->event = WM_LBUTTONDOWN;
+ break;
+ case WebMouseEvent::ButtonMiddle:
+ np_event->event = WM_MBUTTONDOWN;
+ break;
+ case WebMouseEvent::ButtonRight:
+ np_event->event = WM_RBUTTONDOWN;
+ break;
+ }
+ return true;
+ case WebInputEvent::MouseUp:
+ switch (event.button) {
+ case WebMouseEvent::ButtonLeft:
+ np_event->event = WM_LBUTTONUP;
+ break;
+ case WebMouseEvent::ButtonMiddle:
+ np_event->event = WM_MBUTTONUP;
+ break;
+ case WebMouseEvent::ButtonRight:
+ np_event->event = WM_RBUTTONUP;
+ break;
+ }
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+static bool NPEventFromWebKeyboardEvent(const WebKeyboardEvent& event,
+ NPEvent *np_event) {
+ np_event->wParam = event.windowsKeyCode;
+
+ switch (event.type) {
+ case WebInputEvent::KeyDown:
+ np_event->event = WM_KEYDOWN;
+ np_event->lParam = 0;
+ return true;
+ case WebInputEvent::Char:
+ np_event->event = WM_CHAR;
+ np_event->lParam = 0;
+ return true;
+ case WebInputEvent::KeyUp:
+ np_event->event = WM_KEYUP;
+ np_event->lParam = 0x8000;
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+static bool NPEventFromWebInputEvent(const WebInputEvent& event,
+ NPEvent* np_event) {
+ switch (event.type) {
+ case WebInputEvent::MouseMove:
+ case WebInputEvent::MouseLeave:
+ case WebInputEvent::MouseEnter:
+ case WebInputEvent::MouseDown:
+ case WebInputEvent::MouseUp:
+ if (event.size < sizeof(WebMouseEvent)) {
+ NOTREACHED();
+ return false;
+ }
+ return NPEventFromWebMouseEvent(
+ *static_cast<const WebMouseEvent*>(&event), np_event);
+ case WebInputEvent::KeyDown:
+ case WebInputEvent::Char:
+ case WebInputEvent::KeyUp:
+ if (event.size < sizeof(WebKeyboardEvent)) {
+ NOTREACHED();
+ return false;
+ }
+ return NPEventFromWebKeyboardEvent(
+ *static_cast<const WebKeyboardEvent*>(&event), np_event);
+ default:
+ return false;
+ }
+}
+
+bool WebPluginDelegateImpl::PlatformHandleInputEvent(
+ const WebInputEvent& event, WebCursorInfo* cursor_info) {
+ DCHECK(cursor_info != NULL);
+
+ NPEvent np_event;
+ if (!NPEventFromWebInputEvent(event, &np_event)) {
+ return false;
+ }
+
+ // Synchronize the keyboard layout with the one of the browser process. Flash
+ // uses the keyboard layout of this window to verify a WM_CHAR message is
+ // valid. That is, Flash discards a WM_CHAR message unless its character is
+ // the one translated with ToUnicode(). (Since a plug-in is running on a
+ // separate process from the browser process, we need to syncronize it
+ // manually.)
+ if (np_event.event == WM_CHAR) {
+ if (!keyboard_layout_)
+ keyboard_layout_ = GetKeyboardLayout(GetCurrentThreadId());
+ if (!parent_thread_id_)
+ parent_thread_id_ = GetWindowThreadProcessId(parent_, NULL);
+ HKL parent_layout = GetKeyboardLayout(parent_thread_id_);
+ if (keyboard_layout_ != parent_layout) {
+ std::wstring layout_name(base::StringPrintf(L"%08x", parent_layout));
+ LoadKeyboardLayout(layout_name.c_str(), KLF_ACTIVATE);
+ keyboard_layout_ = parent_layout;
+ }
+ }
+
+ if (ShouldTrackEventForModalLoops(&np_event)) {
+ // A windowless plugin can enter a modal loop in a NPP_HandleEvent call.
+ // For e.g. Flash puts up a context menu when we right click on the
+ // windowless plugin area. We detect this by setting up a message filter
+ // hook pror to calling NPP_HandleEvent on the plugin and unhook on
+ // return from NPP_HandleEvent. If the plugin does enter a modal loop
+ // in that context we unhook on receiving the first notification in
+ // the message filter hook.
+ handle_event_message_filter_hook_ =
+ SetWindowsHookEx(WH_MSGFILTER, HandleEventMessageFilterHook, NULL,
+ GetCurrentThreadId());
+ }
+
+ bool old_task_reentrancy_state =
+ MessageLoop::current()->NestableTasksAllowed();
+
+
+ // Maintain a local/global stack for the g_current_plugin_instance variable
+ // as this may be a nested invocation.
+ WebPluginDelegateImpl* last_plugin_instance = g_current_plugin_instance;
+
+ g_current_plugin_instance = this;
+
+ handle_event_depth_++;
+
+ bool ret = instance()->NPP_HandleEvent(&np_event) != 0;
+
+ // Flash and SilverLight always return false, even when they swallow the
+ // event. Flash does this because it passes the event to its window proc,
+ // which is supposed to return 0 if an event was handled. There are few
+ // exceptions, such as IME, where it sometimes returns true.
+ ret = true;
+
+ if (np_event.event == WM_MOUSEMOVE) {
+ // Snag a reference to the current cursor ASAP in case the plugin modified
+ // it. There is a nasty race condition here with the multiprocess browser
+ // as someone might be setting the cursor in the main process as well.
+ current_windowless_cursor_.GetCursorInfo(cursor_info);
+ }
+
+ handle_event_depth_--;
+
+ g_current_plugin_instance = last_plugin_instance;
+
+ MessageLoop::current()->SetNestableTasksAllowed(old_task_reentrancy_state);
+
+ // We could have multiple NPP_HandleEvent calls nested together in case
+ // the plugin enters a modal loop. Reset the pump messages event when
+ // the outermost NPP_HandleEvent call unwinds.
+ if (handle_event_depth_ == 0) {
+ ResetEvent(handle_event_pump_messages_event_);
+ }
+
+ return ret;
+}
+
+
+void WebPluginDelegateImpl::OnModalLoopEntered() {
+ DCHECK(handle_event_pump_messages_event_ != NULL);
+ SetEvent(handle_event_pump_messages_event_);
+
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+
+ UnhookWindowsHookEx(handle_event_message_filter_hook_);
+ handle_event_message_filter_hook_ = NULL;
+}
+
+bool WebPluginDelegateImpl::ShouldTrackEventForModalLoops(NPEvent* event) {
+ if (event->event == WM_RBUTTONDOWN)
+ return true;
+ return false;
+}
+
+void WebPluginDelegateImpl::OnUserGestureEnd() {
+ user_gesture_message_posted_ = false;
+ instance()->PopPopupsEnabledState();
+}
+
+BOOL WINAPI WebPluginDelegateImpl::TrackPopupMenuPatch(
+ HMENU menu, unsigned int flags, int x, int y, int reserved,
+ HWND window, const RECT* rect) {
+
+ HWND last_focus_window = NULL;
+
+ if (g_current_plugin_instance) {
+ unsigned long window_process_id = 0;
+ unsigned long window_thread_id =
+ GetWindowThreadProcessId(window, &window_process_id);
+ // TrackPopupMenu fails if the window passed in belongs to a different
+ // thread.
+ if (::GetCurrentThreadId() != window_thread_id) {
+ window = g_current_plugin_instance->dummy_window_for_activation_;
+ }
+
+ // To ensure that the plugin receives keyboard events we set focus to the
+ // dummy window.
+ // TODO(iyengar) We need a framework in the renderer to identify which
+ // windowless plugin is under the mouse and to handle this. This would
+ // also require some changes in RenderWidgetHost to detect this in the
+ // WM_MOUSEACTIVATE handler and inform the renderer accordingly.
+ if (g_current_plugin_instance->dummy_window_for_activation_) {
+ last_focus_window =
+ ::SetFocus(g_current_plugin_instance->dummy_window_for_activation_);
+ }
+ }
+
+ BOOL result = TrackPopupMenu(menu, flags, x, y, reserved, window, rect);
+
+ if (IsWindow(last_focus_window)) {
+ // The Flash plugin at times sets focus to its hidden top level window
+ // with class name SWFlash_PlaceholderX. This causes the chrome browser
+ // window to receive a WM_ACTIVATEAPP message as a top level window from
+ // another thread is now active. We end up in a state where the chrome
+ // browser window is not active even though the user clicked on it.
+ // Our workaround for this is to send over a raw
+ // WM_LBUTTONDOWN/WM_LBUTTONUP combination to the last focus window, which
+ // does the trick.
+ if (g_current_plugin_instance->dummy_window_for_activation_ !=
+ ::GetFocus()) {
+ INPUT input_info = {0};
+ input_info.type = INPUT_MOUSE;
+ input_info.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
+ ::SendInput(1, &input_info, sizeof(INPUT));
+
+ input_info.type = INPUT_MOUSE;
+ input_info.mi.dwFlags = MOUSEEVENTF_LEFTUP;
+ ::SendInput(1, &input_info, sizeof(INPUT));
+ } else {
+ ::SetFocus(last_focus_window);
+ }
+ }
+
+ return result;
+}
+
+HCURSOR WINAPI WebPluginDelegateImpl::SetCursorPatch(HCURSOR cursor) {
+ // The windowless flash plugin periodically calls SetCursor in a wndproc
+ // instantiated on the plugin thread. This causes annoying cursor flicker
+ // when the mouse is moved on a foreground tab, with a windowless plugin
+ // instance in a background tab. We just ignore the call here.
+ if (!g_current_plugin_instance) {
+ HCURSOR current_cursor = GetCursor();
+ if (current_cursor != cursor) {
+ ::SetCursor(cursor);
+ }
+ return current_cursor;
+ }
+
+ if (!g_current_plugin_instance->IsWindowless()) {
+ return ::SetCursor(cursor);
+ }
+
+ // It is ok to pass NULL here to GetCursor as we are not looking for cursor
+ // types defined by Webkit.
+ HCURSOR previous_cursor =
+ g_current_plugin_instance->current_windowless_cursor_.GetCursor(NULL);
+
+ g_current_plugin_instance->current_windowless_cursor_.InitFromExternalCursor(
+ cursor);
+ return previous_cursor;
+}
+
+LONG WINAPI WebPluginDelegateImpl::RegEnumKeyExWPatch(
+ HKEY key, DWORD index, LPWSTR name, LPDWORD name_size, LPDWORD reserved,
+ LPWSTR class_name, LPDWORD class_size, PFILETIME last_write_time) {
+ DWORD orig_size = *name_size;
+ LONG rv = RegEnumKeyExW(key, index, name, name_size, reserved, class_name,
+ class_size, last_write_time);
+ if (rv == ERROR_SUCCESS &&
+ GetKeyPath(key).find(L"Microsoft\\MediaPlayer\\ShimInclusionList") !=
+ std::wstring::npos) {
+ static const wchar_t kChromeExeName[] = L"chrome.exe";
+ wcsncpy_s(name, orig_size, kChromeExeName, arraysize(kChromeExeName));
+ *name_size =
+ std::min(orig_size, static_cast<DWORD>(arraysize(kChromeExeName)));
+ }
+
+ return rv;
+}
+
+void WebPluginDelegateImpl::HandleCaptureForMessage(HWND window,
+ UINT message) {
+ if (!WebPluginDelegateImpl::IsPluginDelegateWindow(window))
+ return;
+
+ switch (message) {
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ ::SetCapture(window);
+ break;
+
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ ::ReleaseCapture();
+ break;
+
+ default:
+ break;
+ }
+}
diff --git a/webkit/glue/plugins/webplugin_file_delegate.cc b/webkit/glue/plugins/webplugin_file_delegate.cc
new file mode 100644
index 0000000..68c4c60
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_file_delegate.cc
@@ -0,0 +1,17 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webplugin_file_delegate.h"
+
+namespace webkit_glue {
+
+bool WebPluginFileDelegate::ChooseFile(const char* mime_types,
+ int mode,
+ NPChooseFileCallback callback,
+ void* user_data) {
+ return false;
+}
+
+} // namespace webkit_glue
+
diff --git a/webkit/glue/plugins/webplugin_file_delegate.h b/webkit/glue/plugins/webplugin_file_delegate.h
new file mode 100644
index 0000000..ad2bba0
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_file_delegate.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_WEBPLUGIN_FILE_DELEGATE_H_
+#define WEBKIT_GLUE_PLUGINS_WEBPLUGIN_FILE_DELEGATE_H_
+
+#include "base/basictypes.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+
+namespace webkit_glue {
+
+// Interface for the NPAPI file extensions. This class implements "NOP"
+// versions of all these functions so it can be used seamlessly by the
+// "regular" plugin delegate while being overridden by the "pepper" one.
+class WebPluginFileDelegate {
+ public:
+ // See NPChooseFilePtr in npapi_extensions.h. Returns true on success, on
+ // cancel, returns true but *filename will be filled with an empty FilePath
+ // and *handle will be 0.
+ virtual bool ChooseFile(const char* mime_types,
+ int mode,
+ NPChooseFileCallback callback,
+ void* user_data);
+
+ protected:
+ WebPluginFileDelegate() {}
+ virtual ~WebPluginFileDelegate() {}
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_PLUGINS_WEBPLUGIN_FILE_DELEGATE_H_
diff --git a/webkit/glue/plugins/webplugin_impl.cc b/webkit/glue/plugins/webplugin_impl.cc
new file mode 100644
index 0000000..666775b
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_impl.cc
@@ -0,0 +1,1393 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webplugin_impl.h"
+
+#include "base/linked_ptr.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "base/utf_string_conversions.h"
+#include "gfx/rect.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/escape.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_response_headers.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebConsoleMessage.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCookieJar.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCursorInfo.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebDevToolsAgent.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebData.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebHTTPBody.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebHTTPHeaderVisitor.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebKit.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebKitClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPluginContainer.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPluginParams.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLError.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoader.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoaderClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+#include "webkit/appcache/web_application_cache_host_impl.h"
+#include "webkit/glue/multipart_response_delegate.h"
+#include "webkit/glue/plugins/plugin_host.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/webplugin_delegate.h"
+#include "webkit/glue/plugins/webplugin_page_delegate.h"
+
+using appcache::WebApplicationCacheHostImpl;
+using WebKit::WebCanvas;
+using WebKit::WebConsoleMessage;
+using WebKit::WebCookieJar;
+using WebKit::WebCString;
+using WebKit::WebCursorInfo;
+using WebKit::WebData;
+using WebKit::WebDataSource;
+using WebKit::WebDevToolsAgent;
+using WebKit::WebFrame;
+using WebKit::WebHTTPBody;
+using WebKit::WebHTTPHeaderVisitor;
+using WebKit::WebInputEvent;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebMouseEvent;
+using WebKit::WebPluginContainer;
+using WebKit::WebPluginParams;
+using WebKit::WebRect;
+using WebKit::WebString;
+using WebKit::WebURL;
+using WebKit::WebURLError;
+using WebKit::WebURLLoader;
+using WebKit::WebURLLoaderClient;
+using WebKit::WebURLRequest;
+using WebKit::WebURLResponse;
+using WebKit::WebVector;
+using WebKit::WebView;
+using webkit_glue::MultipartResponseDelegate;
+
+namespace webkit_glue {
+namespace {
+
+// This class handles individual multipart responses. It is instantiated when
+// we receive HTTP status code 206 in the HTTP response. This indicates
+// that the response could have multiple parts each separated by a boundary
+// specified in the response header.
+class MultiPartResponseClient : public WebURLLoaderClient {
+ public:
+ explicit MultiPartResponseClient(WebPluginResourceClient* resource_client)
+ : resource_client_(resource_client) {
+ Clear();
+ }
+
+ virtual void willSendRequest(
+ WebURLLoader*, WebURLRequest&, const WebURLResponse&) {}
+ virtual void didSendData(
+ WebURLLoader*, unsigned long long, unsigned long long) {}
+
+ // Called when the multipart parser encounters an embedded multipart
+ // response.
+ virtual void didReceiveResponse(
+ WebURLLoader*, const WebURLResponse& response) {
+ int instance_size;
+ if (!MultipartResponseDelegate::ReadContentRanges(
+ response,
+ &byte_range_lower_bound_,
+ &byte_range_upper_bound_,
+ &instance_size)) {
+ NOTREACHED();
+ return;
+ }
+
+ resource_response_ = response;
+ }
+
+ // Receives individual part data from a multipart response.
+ virtual void didReceiveData(
+ WebURLLoader*, const char* data, int data_size) {
+ // TODO(ananta)
+ // We should defer further loads on multipart resources on the same lines
+ // as regular resources requested by plugins to prevent reentrancy.
+ resource_client_->DidReceiveData(
+ data, data_size, byte_range_lower_bound_);
+ byte_range_lower_bound_ += data_size;
+ }
+
+ virtual void didFinishLoading(WebURLLoader*, double finishTime) {}
+ virtual void didFail(WebURLLoader*, const WebURLError&) {}
+
+ void Clear() {
+ resource_response_.reset();
+ byte_range_lower_bound_ = 0;
+ byte_range_upper_bound_ = 0;
+ }
+
+ private:
+ WebURLResponse resource_response_;
+ // The lower bound of the byte range.
+ int byte_range_lower_bound_;
+ // The upper bound of the byte range.
+ int byte_range_upper_bound_;
+ // The handler for the data.
+ WebPluginResourceClient* resource_client_;
+};
+
+class HeaderFlattener : public WebHTTPHeaderVisitor {
+ public:
+ HeaderFlattener(std::string* buf) : buf_(buf) {
+ }
+
+ virtual void visitHeader(const WebString& name, const WebString& value) {
+ // TODO(darin): Should we really exclude headers with an empty value?
+ if (!name.isEmpty() && !value.isEmpty()) {
+ buf_->append(name.utf8());
+ buf_->append(": ");
+ buf_->append(value.utf8());
+ buf_->append("\n");
+ }
+ }
+
+ private:
+ std::string* buf_;
+};
+
+std::string GetAllHeaders(const WebURLResponse& response) {
+ // TODO(darin): It is possible for httpStatusText to be empty and still have
+ // an interesting response, so this check seems wrong.
+ std::string result;
+ const WebString& status = response.httpStatusText();
+ if (status.isEmpty())
+ return result;
+
+ // TODO(darin): Shouldn't we also report HTTP version numbers?
+ result = base::StringPrintf("HTTP %d ", response.httpStatusCode());
+ result.append(status.utf8());
+ result.append("\n");
+
+ HeaderFlattener flattener(&result);
+ response.visitHTTPHeaderFields(&flattener);
+
+ return result;
+}
+
+struct ResponseInfo {
+ GURL url;
+ std::string mime_type;
+ uint32 last_modified;
+ uint32 expected_length;
+};
+
+void GetResponseInfo(const WebURLResponse& response,
+ ResponseInfo* response_info) {
+ response_info->url = response.url();
+ response_info->mime_type = response.mimeType().utf8();
+
+ // Measured in seconds since 12:00 midnight GMT, January 1, 1970.
+ response_info->last_modified =
+ static_cast<uint32>(response.lastModifiedDate());
+
+ // If the length comes in as -1, then it indicates that it was not
+ // read off the HTTP headers. We replicate Safari webkit behavior here,
+ // which is to set it to 0.
+ response_info->expected_length =
+ static_cast<uint32>(std::max(response.expectedContentLength(), 0LL));
+
+ WebString content_encoding =
+ response.httpHeaderField(WebString::fromUTF8("Content-Encoding"));
+ if (!content_encoding.isNull() &&
+ !EqualsASCII(content_encoding, "identity")) {
+ // Don't send the compressed content length to the plugin, which only
+ // cares about the decoded length.
+ response_info->expected_length = 0;
+ }
+}
+
+} // namespace
+
+// WebKit::WebPlugin ----------------------------------------------------------
+
+struct WebPluginImpl::ClientInfo {
+ unsigned long id;
+ WebPluginResourceClient* client;
+ WebKit::WebURLRequest request;
+ bool pending_failure_notification;
+ linked_ptr<WebKit::WebURLLoader> loader;
+ bool notify_redirects;
+};
+
+bool WebPluginImpl::initialize(WebPluginContainer* container) {
+ if (!page_delegate_)
+ return false;
+
+ WebPluginDelegate* plugin_delegate = page_delegate_->CreatePluginDelegate(
+ file_path_, mime_type_);
+ if (!plugin_delegate)
+ return false;
+
+ // Set the container before Initialize because the plugin may
+ // synchronously call NPN_GetValue to get its container during its
+ // initialization.
+ SetContainer(container);
+ bool ok = plugin_delegate->Initialize(
+ plugin_url_, arg_names_, arg_values_, this, load_manually_);
+ if (!ok) {
+ plugin_delegate->PluginDestroyed();
+ return false;
+ }
+
+ delegate_ = plugin_delegate;
+
+ return true;
+}
+
+void WebPluginImpl::destroy() {
+ SetContainer(NULL);
+ MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+}
+
+NPObject* WebPluginImpl::scriptableObject() {
+ return delegate_->GetPluginScriptableObject();
+}
+
+void WebPluginImpl::paint(WebCanvas* canvas, const WebRect& paint_rect) {
+ if (!delegate_ || !container_)
+ return;
+
+#if defined(OS_WIN)
+ // Force a geometry update if needed to allow plugins like media player
+ // which defer the initial geometry update to work.
+ container_->reportGeometry();
+#endif // OS_WIN
+
+ // Note that |canvas| is only used when in windowless mode.
+ delegate_->Paint(canvas, paint_rect);
+}
+
+void WebPluginImpl::updateGeometry(
+ const WebRect& window_rect, const WebRect& clip_rect,
+ const WebVector<WebRect>& cutout_rects, bool is_visible) {
+ WebPluginGeometry new_geometry;
+ new_geometry.window = window_;
+ new_geometry.window_rect = window_rect;
+ new_geometry.clip_rect = clip_rect;
+ new_geometry.visible = is_visible;
+ new_geometry.rects_valid = true;
+ for (size_t i = 0; i < cutout_rects.size(); ++i)
+ new_geometry.cutout_rects.push_back(cutout_rects[i]);
+
+ // Only send DidMovePlugin if the geometry changed in some way.
+ if (window_ &&
+ page_delegate_ &&
+ (first_geometry_update_ || !new_geometry.Equals(geometry_))) {
+ page_delegate_->DidMovePlugin(new_geometry);
+ }
+
+ // Only UpdateGeometry if either the window or clip rects have changed.
+ if (first_geometry_update_ ||
+ new_geometry.window_rect != geometry_.window_rect ||
+ new_geometry.clip_rect != geometry_.clip_rect) {
+ // Notify the plugin that its parameters have changed.
+ delegate_->UpdateGeometry(new_geometry.window_rect, new_geometry.clip_rect);
+ }
+
+ // Initiate a download on the plugin url. This should be done for the
+ // first update geometry sequence. We need to ensure that the plugin
+ // receives the geometry update before it starts receiving data.
+ if (first_geometry_update_) {
+ // An empty url corresponds to an EMBED tag with no src attribute.
+ if (!load_manually_ && plugin_url_.is_valid()) {
+ // The Flash plugin hangs for a while if it receives data before
+ // receiving valid plugin geometry. By valid geometry we mean the
+ // geometry received by a call to setFrameRect in the Webkit
+ // layout code path. To workaround this issue we download the
+ // plugin source url on a timer.
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, method_factory_.NewRunnableMethod(
+ &WebPluginImpl::OnDownloadPluginSrcUrl), 0);
+ }
+ }
+
+#if defined(OS_WIN)
+ // Don't cache the geometry during the first geometry update. The first
+ // geometry update sequence is received when Widget::setParent is called.
+ // For plugins like media player which have a bug where they only honor
+ // the first geometry update, we have a quirk which ignores the first
+ // geometry update. To ensure that these plugins work correctly in cases
+ // where we receive only one geometry update from webkit, we also force
+ // a geometry update during paint which should go out correctly as the
+ // initial geometry update was not cached.
+ if (!first_geometry_update_)
+ geometry_ = new_geometry;
+#else // OS_WIN
+ geometry_ = new_geometry;
+#endif // OS_WIN
+ first_geometry_update_ = false;
+}
+
+unsigned WebPluginImpl::getBackingTextureId() {
+ // Regular plugins do not have a backing texture.
+ return 0;
+}
+
+void WebPluginImpl::updateFocus(bool focused) {
+ if (accepts_input_events_)
+ delegate_->SetFocus(focused);
+}
+
+void WebPluginImpl::updateVisibility(bool visible) {
+ if (!window_ || !page_delegate_)
+ return;
+
+ WebPluginGeometry move;
+ move.window = window_;
+ move.window_rect = gfx::Rect();
+ move.clip_rect = gfx::Rect();
+ move.rects_valid = false;
+ move.visible = visible;
+
+ page_delegate_->DidMovePlugin(move);
+}
+
+bool WebPluginImpl::acceptsInputEvents() {
+ return accepts_input_events_;
+}
+
+bool WebPluginImpl::handleInputEvent(
+ const WebInputEvent& event, WebCursorInfo& cursor_info) {
+ // Swallow context menu events in order to suppress the default context menu.
+ if (event.type == WebInputEvent::ContextMenu)
+ return true;
+
+ return delegate_->HandleInputEvent(event, &cursor_info);
+}
+
+void WebPluginImpl::didReceiveResponse(const WebURLResponse& response) {
+ ignore_response_error_ = false;
+
+ ResponseInfo response_info;
+ GetResponseInfo(response, &response_info);
+
+ delegate_->DidReceiveManualResponse(
+ response_info.url,
+ response_info.mime_type,
+ GetAllHeaders(response),
+ response_info.expected_length,
+ response_info.last_modified);
+}
+
+void WebPluginImpl::didReceiveData(const char* data, int data_length) {
+ delegate_->DidReceiveManualData(data, data_length);
+}
+
+void WebPluginImpl::didFinishLoading() {
+ delegate_->DidFinishManualLoading();
+}
+
+void WebPluginImpl::didFailLoading(const WebURLError& error) {
+ if (!ignore_response_error_)
+ delegate_->DidManualLoadFail();
+}
+
+void WebPluginImpl::didFinishLoadingFrameRequest(
+ const WebURL& url, void* notify_data) {
+ if (delegate_) {
+ // We're converting a void* into an arbitrary int id. Though
+ // these types are the same size on all the platforms we support,
+ // the compiler may complain as though they are different, so to
+ // make the casting gods happy go through an intptr_t (the union
+ // of void* and int) rather than converting straight across.
+ delegate_->DidFinishLoadWithReason(
+ url, NPRES_DONE, reinterpret_cast<intptr_t>(notify_data));
+ }
+}
+
+void WebPluginImpl::didFailLoadingFrameRequest(
+ const WebURL& url, void* notify_data, const WebURLError& error) {
+ if (!delegate_)
+ return;
+
+ NPReason reason =
+ error.reason == net::ERR_ABORTED ? NPRES_USER_BREAK : NPRES_NETWORK_ERR;
+ // See comment in didFinishLoadingFrameRequest about the cast here.
+ delegate_->DidFinishLoadWithReason(
+ url, reason, reinterpret_cast<intptr_t>(notify_data));
+}
+
+bool WebPluginImpl::supportsPaginatedPrint() {
+ if (!delegate_)
+ return false;
+ return delegate_->PrintSupportsPrintExtension();
+}
+
+int WebPluginImpl::printBegin(const WebRect& printable_area, int printer_dpi) {
+ if (!delegate_)
+ return 0;
+
+ if (!supportsPaginatedPrint())
+ return 0;
+
+ return delegate_->PrintBegin(printable_area, printer_dpi);
+}
+
+bool WebPluginImpl::printPage(int page_number, WebCanvas* canvas) {
+ if (!delegate_)
+ return false;
+
+ return delegate_->PrintPage(page_number, canvas);
+}
+
+void WebPluginImpl::printEnd() {
+ if (delegate_)
+ delegate_->PrintEnd();
+}
+
+bool WebPluginImpl::hasSelection() const {
+ if (!delegate_)
+ return false;
+
+ return delegate_->HasSelection();
+}
+
+WebKit::WebString WebPluginImpl::selectionAsText() const {
+ if (!delegate_)
+ return WebString();
+
+ return delegate_->GetSelectionAsText();
+}
+
+WebKit::WebString WebPluginImpl::selectionAsMarkup() const {
+ if (!delegate_)
+ return WebString();
+
+ return delegate_->GetSelectionAsMarkup();
+}
+
+void WebPluginImpl::setZoomFactor(float scale, bool text_only) {
+ if (delegate_)
+ delegate_->SetZoomFactor(scale, text_only);
+}
+
+bool WebPluginImpl::startFind(const WebKit::WebString& search_text,
+ bool case_sensitive,
+ int identifier) {
+ if (!delegate_)
+ return false;
+ return delegate_->StartFind(search_text, case_sensitive, identifier);
+}
+
+void WebPluginImpl::selectFindResult(bool forward) {
+ if (delegate_)
+ delegate_->SelectFindResult(forward);
+}
+
+void WebPluginImpl::stopFind() {
+ if (delegate_)
+ delegate_->StopFind();
+}
+
+
+// -----------------------------------------------------------------------------
+
+WebPluginImpl::WebPluginImpl(
+ WebFrame* webframe,
+ const WebPluginParams& params,
+ const FilePath& file_path,
+ const std::string& mime_type,
+ const base::WeakPtr<WebPluginPageDelegate>& page_delegate)
+ : windowless_(false),
+ window_(gfx::kNullPluginWindow),
+ accepts_input_events_(false),
+ page_delegate_(page_delegate),
+ webframe_(webframe),
+ delegate_(NULL),
+ container_(NULL),
+ plugin_url_(params.url),
+ load_manually_(params.loadManually),
+ first_geometry_update_(true),
+ ignore_response_error_(false),
+ file_path_(file_path),
+ mime_type_(mime_type),
+ ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
+ DCHECK_EQ(params.attributeNames.size(), params.attributeValues.size());
+ StringToLowerASCII(&mime_type_);
+
+ for (size_t i = 0; i < params.attributeNames.size(); ++i) {
+ arg_names_.push_back(params.attributeNames[i].utf8());
+ arg_values_.push_back(params.attributeValues[i].utf8());
+ }
+}
+
+WebPluginImpl::~WebPluginImpl() {
+}
+
+void WebPluginImpl::SetWindow(gfx::PluginWindowHandle window) {
+#if defined(OS_MACOSX)
+ // The only time this is called twice, and the second time with a
+ // non-zero PluginWindowHandle, is the case when this WebPluginImpl
+ // is created on behalf of the GPU plugin. This entire code path
+ // will go away soon, as soon as the GPU plugin becomes the GPU
+ // process, so it is being separated out for easy deletion.
+
+ // The logic we want here is: if (window) DCHECK(!window_);
+ DCHECK(!(window_ && window));
+ window_ = window;
+ // Lie to ourselves about being windowless even if we got a fake
+ // plugin window handle, so we continue to get input events.
+ windowless_ = true;
+ accepts_input_events_ = true;
+ // We do not really need to notify the page delegate that a plugin
+ // window was created -- so don't.
+#else
+ if (window) {
+ DCHECK(!windowless_);
+ window_ = window;
+ accepts_input_events_ = false;
+ if (page_delegate_) {
+ // Tell the view delegate that the plugin window was created, so that it
+ // can create necessary container widgets.
+ page_delegate_->CreatedPluginWindow(window);
+ }
+ } else {
+ DCHECK(!window_); // Make sure not called twice.
+ windowless_ = true;
+ accepts_input_events_ = true;
+ }
+#endif
+}
+
+void WebPluginImpl::SetAcceptsInputEvents(bool accepts) {
+ accepts_input_events_ = accepts;
+}
+
+void WebPluginImpl::WillDestroyWindow(gfx::PluginWindowHandle window) {
+ DCHECK_EQ(window, window_);
+ window_ = gfx::kNullPluginWindow;
+ if (page_delegate_)
+ page_delegate_->WillDestroyPluginWindow(window);
+}
+
+GURL WebPluginImpl::CompleteURL(const char* url) {
+ if (!webframe_) {
+ NOTREACHED();
+ return GURL();
+ }
+ // TODO(darin): Is conversion from UTF8 correct here?
+ return webframe_->document().completeURL(WebString::fromUTF8(url));
+}
+
+void WebPluginImpl::CancelResource(unsigned long id) {
+ for (size_t i = 0; i < clients_.size(); ++i) {
+ if (clients_[i].id == id) {
+ if (clients_[i].loader.get()) {
+ clients_[i].loader->setDefersLoading(false);
+ clients_[i].loader->cancel();
+ RemoveClient(i);
+ }
+ return;
+ }
+ }
+}
+
+bool WebPluginImpl::SetPostData(WebURLRequest* request,
+ const char *buf,
+ uint32 length) {
+ std::vector<std::string> names;
+ std::vector<std::string> values;
+ std::vector<char> body;
+ bool rv = NPAPI::PluginHost::SetPostData(buf, length, &names, &values, &body);
+
+ for (size_t i = 0; i < names.size(); ++i) {
+ request->addHTTPHeaderField(WebString::fromUTF8(names[i]),
+ WebString::fromUTF8(values[i]));
+ }
+
+ WebString content_type_header = WebString::fromUTF8("Content-Type");
+ const WebString& content_type =
+ request->httpHeaderField(content_type_header);
+ if (content_type.isEmpty()) {
+ request->setHTTPHeaderField(
+ content_type_header,
+ WebString::fromUTF8("application/x-www-form-urlencoded"));
+ }
+
+ WebHTTPBody http_body;
+ if (body.size()) {
+ http_body.initialize();
+ http_body.appendData(WebData(&body[0], body.size()));
+ }
+ request->setHTTPBody(http_body);
+
+ return rv;
+}
+
+WebPluginDelegate* WebPluginImpl::delegate() {
+ return delegate_;
+}
+
+bool WebPluginImpl::IsValidUrl(const GURL& url, Referrer referrer_flag) {
+ if (referrer_flag == PLUGIN_SRC &&
+ mime_type_ == "application/x-shockwave-flash" &&
+ url.GetOrigin() != plugin_url_.GetOrigin()) {
+ // Do url check to make sure that there are no @, ;, \ chars in between url
+ // scheme and url path.
+ const char* url_to_check(url.spec().data());
+ url_parse::Parsed parsed;
+ url_parse::ParseStandardURL(url_to_check, strlen(url_to_check), &parsed);
+ if (parsed.path.begin <= parsed.scheme.end())
+ return true;
+ std::string string_to_search;
+ string_to_search.assign(url_to_check + parsed.scheme.end(),
+ parsed.path.begin - parsed.scheme.end());
+ if (string_to_search.find("@") != std::string::npos ||
+ string_to_search.find(";") != std::string::npos ||
+ string_to_search.find("\\") != std::string::npos)
+ return false;
+ }
+
+ return true;
+}
+
+WebPluginImpl::RoutingStatus WebPluginImpl::RouteToFrame(
+ const char* url,
+ bool is_javascript_url,
+ const char* method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ int notify_id,
+ Referrer referrer_flag) {
+ // If there is no target, there is nothing to do
+ if (!target)
+ return NOT_ROUTED;
+
+ // This could happen if the WebPluginContainer was already deleted.
+ if (!webframe_)
+ return NOT_ROUTED;
+
+ WebString target_str = WebString::fromUTF8(target);
+
+ // Take special action for JavaScript URLs
+ if (is_javascript_url) {
+ WebFrame* target_frame =
+ webframe_->view()->findFrameByName(target_str, webframe_);
+ // For security reasons, do not allow JavaScript on frames
+ // other than this frame.
+ if (target_frame != webframe_) {
+ // TODO(darin): Localize this message.
+ const char kMessage[] =
+ "Ignoring cross-frame javascript URL load requested by plugin.";
+ webframe_->addMessageToConsole(
+ WebConsoleMessage(WebConsoleMessage::LevelError,
+ WebString::fromUTF8(kMessage)));
+ return ROUTED;
+ }
+
+ // Route javascript calls back to the plugin.
+ return NOT_ROUTED;
+ }
+
+ // If we got this far, we're routing content to a target frame.
+ // Go fetch the URL.
+
+ GURL complete_url = CompleteURL(url);
+ // Remove when flash bug is fixed. http://crbug.com/40016.
+ if (!WebPluginImpl::IsValidUrl(complete_url, referrer_flag))
+ return INVALID_URL;
+
+ if (strcmp(method, "GET") != 0) {
+ // We're only going to route HTTP/HTTPS requests
+ if (!(complete_url.SchemeIs("http") || complete_url.SchemeIs("https")))
+ return INVALID_URL;
+ }
+
+ WebURLRequest request(complete_url);
+ SetReferrer(&request, referrer_flag);
+
+ request.setHTTPMethod(WebString::fromUTF8(method));
+ request.setFirstPartyForCookies(
+ webframe_->document().firstPartyForCookies());
+ if (len > 0) {
+ if (!SetPostData(&request, buf, len)) {
+ // Uhoh - we're in trouble. There isn't a good way
+ // to recover at this point. Break out.
+ NOTREACHED();
+ return ROUTED;
+ }
+ }
+
+ container_->loadFrameRequest(
+ request, target_str, notify_id != 0, reinterpret_cast<void*>(notify_id));
+ return ROUTED;
+}
+
+NPObject* WebPluginImpl::GetWindowScriptNPObject() {
+ if (!webframe_) {
+ NOTREACHED();
+ return NULL;
+ }
+ return webframe_->windowObject();
+}
+
+NPObject* WebPluginImpl::GetPluginElement() {
+ return container_->scriptableObjectForElement();
+}
+
+void WebPluginImpl::SetCookie(const GURL& url,
+ const GURL& first_party_for_cookies,
+ const std::string& cookie) {
+ if (!page_delegate_)
+ return;
+
+ WebCookieJar* cookie_jar = page_delegate_->GetCookieJar();
+ if (!cookie_jar) {
+ DLOG(WARNING) << "No cookie jar!";
+ return;
+ }
+
+ cookie_jar->setCookie(
+ url, first_party_for_cookies, WebString::fromUTF8(cookie));
+}
+
+std::string WebPluginImpl::GetCookies(const GURL& url,
+ const GURL& first_party_for_cookies) {
+ if (!page_delegate_)
+ return std::string();
+
+ WebCookieJar* cookie_jar = page_delegate_->GetCookieJar();
+ if (!cookie_jar) {
+ DLOG(WARNING) << "No cookie jar!";
+ return std::string();
+ }
+
+ return UTF16ToUTF8(cookie_jar->cookies(url, first_party_for_cookies));
+}
+
+void WebPluginImpl::ShowModalHTMLDialog(const GURL& url, int width, int height,
+ const std::string& json_arguments,
+ std::string* json_retval) {
+ if (page_delegate_) {
+ page_delegate_->ShowModalHTMLDialogForPlugin(
+ url, gfx::Size(width, height), json_arguments, json_retval);
+ }
+}
+
+void WebPluginImpl::OnMissingPluginStatus(int status) {
+ NOTREACHED();
+}
+
+void WebPluginImpl::URLRedirectResponse(bool allow, int resource_id) {
+ for (size_t i = 0; i < clients_.size(); ++i) {
+ if (clients_[i].id == static_cast<unsigned long>(resource_id)) {
+ if (clients_[i].loader.get()) {
+ if (allow) {
+ clients_[i].loader->setDefersLoading(false);
+ } else {
+ clients_[i].loader->cancel();
+ clients_[i].client->DidFail();
+ }
+ }
+ break;
+ }
+ }
+}
+
+void WebPluginImpl::Invalidate() {
+ if (container_)
+ container_->invalidate();
+}
+
+void WebPluginImpl::InvalidateRect(const gfx::Rect& rect) {
+ if (container_)
+ container_->invalidateRect(rect);
+}
+
+void WebPluginImpl::OnDownloadPluginSrcUrl() {
+ HandleURLRequestInternal(
+ plugin_url_.spec().c_str(), "GET", NULL, NULL, 0, 0, false, DOCUMENT_URL,
+ false);
+}
+
+WebPluginResourceClient* WebPluginImpl::GetClientFromLoader(
+ WebURLLoader* loader) {
+ ClientInfo* client_info = GetClientInfoFromLoader(loader);
+ if (client_info)
+ return client_info->client;
+ return NULL;
+}
+
+WebPluginImpl::ClientInfo* WebPluginImpl::GetClientInfoFromLoader(
+ WebURLLoader* loader) {
+ for (size_t i = 0; i < clients_.size(); ++i) {
+ if (clients_[i].loader.get() == loader)
+ return &clients_[i];
+ }
+
+ NOTREACHED();
+ return 0;
+}
+
+void WebPluginImpl::willSendRequest(WebURLLoader* loader,
+ WebURLRequest& request,
+ const WebURLResponse& response) {
+ WebPluginImpl::ClientInfo* client_info = GetClientInfoFromLoader(loader);
+ if (client_info) {
+ if (net::HttpResponseHeaders::IsRedirectResponseCode(
+ response.httpStatusCode())) {
+ // If the plugin does not participate in url redirect notifications then
+ // just block cross origin 307 POST redirects.
+ if (!client_info->notify_redirects) {
+ if (response.httpStatusCode() == 307 &&
+ LowerCaseEqualsASCII(request.httpMethod().utf8(), "post")) {
+ GURL original_request_url(response.url());
+ GURL response_url(request.url());
+ if (original_request_url.GetOrigin() != response_url.GetOrigin()) {
+ loader->setDefersLoading(true);
+ loader->cancel();
+ client_info->client->DidFail();
+ return;
+ }
+ }
+ } else {
+ loader->setDefersLoading(true);
+ }
+ }
+ client_info->client->WillSendRequest(request.url(),
+ response.httpStatusCode());
+ }
+}
+
+void WebPluginImpl::didSendData(WebURLLoader* loader,
+ unsigned long long bytes_sent,
+ unsigned long long total_bytes_to_be_sent) {
+}
+
+void WebPluginImpl::didReceiveResponse(WebURLLoader* loader,
+ const WebURLResponse& response) {
+ static const int kHttpPartialResponseStatusCode = 206;
+ static const int kHttpResponseSuccessStatusCode = 200;
+
+ WebPluginResourceClient* client = GetClientFromLoader(loader);
+ if (!client)
+ return;
+
+ ResponseInfo response_info;
+ GetResponseInfo(response, &response_info);
+
+ bool request_is_seekable = true;
+ if (client->IsMultiByteResponseExpected()) {
+ if (response.httpStatusCode() == kHttpPartialResponseStatusCode) {
+ HandleHttpMultipartResponse(response, client);
+ return;
+ } else if (response.httpStatusCode() == kHttpResponseSuccessStatusCode) {
+ // If the client issued a byte range request and the server responds with
+ // HTTP 200 OK, it indicates that the server does not support byte range
+ // requests.
+ // We need to emulate Firefox behavior by doing the following:-
+ // 1. Destroy the plugin instance in the plugin process. Ensure that
+ // existing resource requests initiated for the plugin instance
+ // continue to remain valid.
+ // 2. Create a new plugin instance and notify it about the response
+ // received here.
+ if (!ReinitializePluginForResponse(loader)) {
+ NOTREACHED();
+ return;
+ }
+
+ // The server does not support byte range requests. No point in creating
+ // seekable streams.
+ request_is_seekable = false;
+
+ delete client;
+ client = NULL;
+
+ // Create a new resource client for this request.
+ for (size_t i = 0; i < clients_.size(); ++i) {
+ if (clients_[i].loader.get() == loader) {
+ WebPluginResourceClient* resource_client =
+ delegate_->CreateResourceClient(clients_[i].id, plugin_url_, 0);
+ clients_[i].client = resource_client;
+ client = resource_client;
+ break;
+ }
+ }
+
+ DCHECK(client != NULL);
+ }
+ }
+
+ // Calling into a plugin could result in reentrancy if the plugin yields
+ // control to the OS like entering a modal loop etc. Prevent this by
+ // stopping further loading until the plugin notifies us that it is ready to
+ // accept data
+ loader->setDefersLoading(true);
+
+ client->DidReceiveResponse(
+ response_info.mime_type,
+ GetAllHeaders(response),
+ response_info.expected_length,
+ response_info.last_modified,
+ request_is_seekable);
+
+ if (WebDevToolsAgent* devtools_agent = GetDevToolsAgent()) {
+ ClientInfo* client_info = GetClientInfoFromLoader(loader);
+ if (client_info)
+ devtools_agent->didReceiveResponse(client_info->id, response);
+ }
+
+ // Bug http://b/issue?id=925559. The flash plugin would not handle the HTTP
+ // error codes in the stream header and as a result, was unaware of the
+ // fate of the HTTP requests issued via NPN_GetURLNotify. Webkit and FF
+ // destroy the stream and invoke the NPP_DestroyStream function on the
+ // plugin if the HTTP request fails.
+ const GURL& url = response.url();
+ if (url.SchemeIs("http") || url.SchemeIs("https")) {
+ if (response.httpStatusCode() < 100 || response.httpStatusCode() >= 400) {
+ // The plugin instance could be in the process of deletion here.
+ // Verify if the WebPluginResourceClient instance still exists before
+ // use.
+ ClientInfo* client_info = GetClientInfoFromLoader(loader);
+ if (client_info) {
+ client_info->pending_failure_notification = true;
+ }
+ }
+ }
+}
+
+void WebPluginImpl::didReceiveData(WebURLLoader* loader,
+ const char *buffer,
+ int length) {
+ WebPluginResourceClient* client = GetClientFromLoader(loader);
+ if (!client)
+ return;
+
+ // ClientInfo can be removed from clients_ vector by next statements.
+ if (WebDevToolsAgent* devtools_agent = GetDevToolsAgent()) {
+ ClientInfo* client_info = GetClientInfoFromLoader(loader);
+ if (client_info)
+ devtools_agent->didReceiveData(client_info->id, length);
+ }
+ MultiPartResponseHandlerMap::iterator index =
+ multi_part_response_map_.find(client);
+ if (index != multi_part_response_map_.end()) {
+ MultipartResponseDelegate* multi_part_handler = (*index).second;
+ DCHECK(multi_part_handler != NULL);
+ multi_part_handler->OnReceivedData(buffer, length);
+ } else {
+ loader->setDefersLoading(true);
+ client->DidReceiveData(buffer, length, 0);
+ }
+}
+
+void WebPluginImpl::didFinishLoading(WebURLLoader* loader, double finishTime) {
+ ClientInfo* client_info = GetClientInfoFromLoader(loader);
+ if (client_info && client_info->client) {
+ MultiPartResponseHandlerMap::iterator index =
+ multi_part_response_map_.find(client_info->client);
+ if (index != multi_part_response_map_.end()) {
+ delete (*index).second;
+ multi_part_response_map_.erase(index);
+ if (page_delegate_)
+ page_delegate_->DidStopLoadingForPlugin();
+ }
+ loader->setDefersLoading(true);
+ WebPluginResourceClient* resource_client = client_info->client;
+ // The ClientInfo can get deleted in the call to DidFinishLoading below.
+ // It is not safe to access this structure after that.
+ client_info->client = NULL;
+ resource_client->DidFinishLoading();
+
+ if (WebDevToolsAgent* devtools_agent = GetDevToolsAgent())
+ devtools_agent->didFinishLoading(client_info->id);
+ }
+}
+
+void WebPluginImpl::didFail(WebURLLoader* loader,
+ const WebURLError& error) {
+ ClientInfo* client_info = GetClientInfoFromLoader(loader);
+ if (client_info && client_info->client) {
+ loader->setDefersLoading(true);
+ WebPluginResourceClient* resource_client = client_info->client;
+ // The ClientInfo can get deleted in the call to DidFail below.
+ // It is not safe to access this structure after that.
+ client_info->client = NULL;
+ resource_client->DidFail();
+
+ if (WebDevToolsAgent* devtools_agent = GetDevToolsAgent())
+ devtools_agent->didFailLoading(client_info->id, error);
+ }
+}
+
+void WebPluginImpl::RemoveClient(size_t i) {
+ clients_.erase(clients_.begin() + i);
+}
+
+void WebPluginImpl::RemoveClient(WebURLLoader* loader) {
+ for (size_t i = 0; i < clients_.size(); ++i) {
+ if (clients_[i].loader.get() == loader) {
+ RemoveClient(i);
+ return;
+ }
+ }
+}
+
+void WebPluginImpl::SetContainer(WebPluginContainer* container) {
+ if (!container)
+ TearDownPluginInstance(NULL);
+ container_ = container;
+}
+
+void WebPluginImpl::HandleURLRequest(const char* url,
+ const char* method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ int notify_id,
+ bool popups_allowed,
+ bool notify_redirects) {
+ // GetURL/PostURL requests initiated explicitly by plugins should specify the
+ // plugin SRC url as the referrer if it is available.
+ HandleURLRequestInternal(
+ url, method, target, buf, len, notify_id, popups_allowed, PLUGIN_SRC,
+ notify_redirects);
+}
+
+void WebPluginImpl::HandleURLRequestInternal(const char* url,
+ const char* method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ int notify_id,
+ bool popups_allowed,
+ Referrer referrer_flag,
+ bool notify_redirects) {
+ // For this request, we either route the output to a frame
+ // because a target has been specified, or we handle the request
+ // here, i.e. by executing the script if it is a javascript url
+ // or by initiating a download on the URL, etc. There is one special
+ // case in that the request is a javascript url and the target is "_self",
+ // in which case we route the output to the plugin rather than routing it
+ // to the plugin's frame.
+ bool is_javascript_url = StartsWithASCII(url, "javascript:", false);
+ RoutingStatus routing_status = RouteToFrame(
+ url, is_javascript_url, method, target, buf, len, notify_id,
+ referrer_flag);
+ if (routing_status == ROUTED)
+ return;
+
+ if (is_javascript_url) {
+ GURL gurl(url);
+ WebString result = container_->executeScriptURL(gurl, popups_allowed);
+
+ // delegate_ could be NULL because executeScript caused the container to
+ // be deleted.
+ if (delegate_) {
+ delegate_->SendJavaScriptStream(
+ gurl, result.utf8(), !result.isNull(), notify_id);
+ }
+
+ return;
+ }
+
+ unsigned long resource_id = GetNextResourceId();
+ if (!resource_id)
+ return;
+
+ GURL complete_url = CompleteURL(url);
+ // Remove when flash bug is fixed. http://crbug.com/40016.
+ if (!WebPluginImpl::IsValidUrl(complete_url, referrer_flag))
+ return;
+
+ WebPluginResourceClient* resource_client = delegate_->CreateResourceClient(
+ resource_id, complete_url, notify_id);
+ if (!resource_client)
+ return;
+
+ // If the RouteToFrame call returned a failure then inform the result
+ // back to the plugin asynchronously.
+ if ((routing_status == INVALID_URL) ||
+ (routing_status == GENERAL_FAILURE)) {
+ resource_client->DidFail();
+ return;
+ }
+
+ // CreateResourceClient() sends a synchronous IPC message so it's possible
+ // that TearDownPluginInstance() may have been called in the nested
+ // message loop. If so, don't start the request.
+ if (!delegate_)
+ return;
+
+ InitiateHTTPRequest(resource_id, resource_client, complete_url, method, buf,
+ len, NULL, referrer_flag, notify_redirects);
+}
+
+unsigned long WebPluginImpl::GetNextResourceId() {
+ if (!webframe_)
+ return 0;
+ WebView* view = webframe_->view();
+ if (!view)
+ return 0;
+ return view->createUniqueIdentifierForRequest();
+}
+
+bool WebPluginImpl::InitiateHTTPRequest(unsigned long resource_id,
+ WebPluginResourceClient* client,
+ const GURL& url,
+ const char* method,
+ const char* buf,
+ int buf_len,
+ const char* range_info,
+ Referrer referrer_flag,
+ bool notify_redirects) {
+ if (!client) {
+ NOTREACHED();
+ return false;
+ }
+
+ ClientInfo info;
+ info.id = resource_id;
+ info.client = client;
+ info.request.initialize();
+ info.request.setURL(url);
+ info.request.setFirstPartyForCookies(
+ webframe_->document().firstPartyForCookies());
+ info.request.setRequestorProcessID(delegate_->GetProcessId());
+ info.request.setTargetType(WebURLRequest::TargetIsObject);
+ info.request.setHTTPMethod(WebString::fromUTF8(method));
+ info.pending_failure_notification = false;
+ info.notify_redirects = notify_redirects;
+
+ if (range_info) {
+ info.request.addHTTPHeaderField(WebString::fromUTF8("Range"),
+ WebString::fromUTF8(range_info));
+ }
+
+ if (strcmp(method, "POST") == 0) {
+ // Adds headers or form data to a request. This must be called before
+ // we initiate the actual request.
+ SetPostData(&info.request, buf, buf_len);
+ }
+
+ SetReferrer(&info.request, referrer_flag);
+
+ // Sets the routing id to associate the ResourceRequest with the RenderView.
+ webframe_->dispatchWillSendRequest(info.request);
+
+ // Sets the appcache host id to allow retrieval from the appcache.
+ if (WebApplicationCacheHostImpl* appcache_host =
+ WebApplicationCacheHostImpl::FromFrame(webframe_)) {
+ appcache_host->willStartSubResourceRequest(info.request);
+ }
+
+ if (WebDevToolsAgent* devtools_agent = GetDevToolsAgent()) {
+ devtools_agent->identifierForInitialRequest(resource_id, webframe_,
+ info.request);
+ devtools_agent->willSendRequest(resource_id, info.request);
+ }
+
+ info.loader.reset(WebKit::webKitClient()->createURLLoader());
+ if (!info.loader.get())
+ return false;
+ info.loader->loadAsynchronously(info.request, this);
+
+ clients_.push_back(info);
+ return true;
+}
+
+void WebPluginImpl::CancelDocumentLoad() {
+ if (webframe_) {
+ ignore_response_error_ = true;
+ webframe_->stopLoading();
+ }
+}
+
+void WebPluginImpl::InitiateHTTPRangeRequest(
+ const char* url, const char* range_info, int range_request_id) {
+ unsigned long resource_id = GetNextResourceId();
+ if (!resource_id)
+ return;
+
+ GURL complete_url = CompleteURL(url);
+ // Remove when flash bug is fixed. http://crbug.com/40016.
+ if (!WebPluginImpl::IsValidUrl(complete_url,
+ load_manually_ ? NO_REFERRER : PLUGIN_SRC))
+ return;
+
+ WebPluginResourceClient* resource_client =
+ delegate_->CreateSeekableResourceClient(resource_id, range_request_id);
+ InitiateHTTPRequest(
+ resource_id, resource_client, complete_url, "GET", NULL, 0, range_info,
+ load_manually_ ? NO_REFERRER : PLUGIN_SRC, false);
+}
+
+void WebPluginImpl::SetDeferResourceLoading(unsigned long resource_id,
+ bool defer) {
+ std::vector<ClientInfo>::iterator client_index = clients_.begin();
+ while (client_index != clients_.end()) {
+ ClientInfo& client_info = *client_index;
+
+ if (client_info.id == resource_id) {
+ client_info.loader->setDefersLoading(defer);
+
+ // If we determined that the request had failed via the HTTP headers
+ // in the response then we send out a failure notification to the
+ // plugin process, as certain plugins don't handle HTTP failure codes
+ // correctly.
+ if (!defer && client_info.client &&
+ client_info.pending_failure_notification) {
+ // The ClientInfo and the iterator can become invalid due to the call
+ // to DidFail below.
+ WebPluginResourceClient* resource_client = client_info.client;
+ client_info.loader->cancel();
+ clients_.erase(client_index++);
+ resource_client->DidFail();
+
+ // Report that resource loading finished.
+ if (WebDevToolsAgent* devtools_agent = GetDevToolsAgent())
+ devtools_agent->didFinishLoading(resource_id);
+ }
+ break;
+ }
+ client_index++;
+ }
+}
+
+bool WebPluginImpl::IsOffTheRecord() {
+ return false;
+}
+
+void WebPluginImpl::HandleHttpMultipartResponse(
+ const WebURLResponse& response, WebPluginResourceClient* client) {
+ std::string multipart_boundary;
+ if (!MultipartResponseDelegate::ReadMultipartBoundary(
+ response, &multipart_boundary)) {
+ NOTREACHED();
+ return;
+ }
+
+ if (page_delegate_)
+ page_delegate_->DidStartLoadingForPlugin();
+
+ MultiPartResponseClient* multi_part_response_client =
+ new MultiPartResponseClient(client);
+
+ MultipartResponseDelegate* multi_part_response_handler =
+ new MultipartResponseDelegate(multi_part_response_client, NULL,
+ response,
+ multipart_boundary);
+ multi_part_response_map_[client] = multi_part_response_handler;
+}
+
+bool WebPluginImpl::ReinitializePluginForResponse(
+ WebURLLoader* loader) {
+ WebFrame* webframe = webframe_;
+ if (!webframe)
+ return false;
+
+ WebView* webview = webframe->view();
+ if (!webview)
+ return false;
+
+ WebPluginContainer* container_widget = container_;
+
+ // Destroy the current plugin instance.
+ TearDownPluginInstance(loader);
+
+ container_ = container_widget;
+ webframe_ = webframe;
+
+ WebPluginDelegate* plugin_delegate = page_delegate_->CreatePluginDelegate(
+ file_path_, mime_type_);
+
+ bool ok = plugin_delegate && plugin_delegate->Initialize(
+ plugin_url_, arg_names_, arg_values_, this, load_manually_);
+
+ if (!ok) {
+ container_ = NULL;
+ // TODO(iyengar) Should we delete the current plugin instance here?
+ return false;
+ }
+
+ delegate_ = plugin_delegate;
+
+ // Force a geometry update to occur to ensure that the plugin becomes
+ // visible.
+ container_->reportGeometry();
+
+ // The plugin move sequences accumulated via DidMove are sent to the browser
+ // whenever the renderer paints. Force a paint here to ensure that changes
+ // to the plugin window are propagated to the browser.
+ container_->invalidate();
+ return true;
+}
+
+void WebPluginImpl::TearDownPluginInstance(
+ WebURLLoader* loader_to_ignore) {
+ // The container maintains a list of JSObjects which are related to this
+ // plugin. Tell the frame we're gone so that it can invalidate all of
+ // those sub JSObjects.
+ if (container_)
+ container_->clearScriptObjects();
+
+ if (delegate_) {
+ // Call PluginDestroyed() first to prevent the plugin from calling us back
+ // in the middle of tearing down the render tree.
+ delegate_->PluginDestroyed();
+ delegate_ = NULL;
+ }
+
+ // Cancel any pending requests because otherwise this deleted object will
+ // be called by the ResourceDispatcher.
+ std::vector<ClientInfo>::iterator client_index = clients_.begin();
+ while (client_index != clients_.end()) {
+ ClientInfo& client_info = *client_index;
+
+ if (loader_to_ignore == client_info.loader) {
+ client_index++;
+ continue;
+ }
+
+ if (client_info.loader.get())
+ client_info.loader->cancel();
+
+ client_index = clients_.erase(client_index);
+ }
+
+ // This needs to be called now and not in the destructor since the
+ // webframe_ might not be valid anymore.
+ webframe_ = NULL;
+ method_factory_.RevokeAll();
+}
+
+void WebPluginImpl::SetReferrer(WebKit::WebURLRequest* request,
+ Referrer referrer_flag) {
+ switch (referrer_flag) {
+ case DOCUMENT_URL:
+ webframe_->setReferrerForRequest(*request, GURL());
+ break;
+
+ case PLUGIN_SRC:
+ webframe_->setReferrerForRequest(*request, plugin_url_);
+ break;
+
+ default:
+ break;
+ }
+}
+
+WebDevToolsAgent* WebPluginImpl::GetDevToolsAgent() {
+ if (!webframe_)
+ return NULL;
+ WebView* view = webframe_->view();
+ if (!view)
+ return NULL;
+ return view->devToolsAgent();
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/plugins/webplugin_impl.h b/webkit/glue/plugins/webplugin_impl.h
new file mode 100644
index 0000000..27a692b
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_impl.h
@@ -0,0 +1,330 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBPLUGIN_IMPL_H_
+#define WEBKIT_GLUE_WEBPLUGIN_IMPL_H_
+
+#include <string>
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/task.h"
+#include "base/weak_ptr.h"
+#include "gfx/native_widget_types.h"
+#include "googleurl/src/gurl.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPlugin.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebRect.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLLoaderClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebVector.h"
+#include "webkit/glue/plugins/webplugin.h"
+
+class WebViewDelegate;
+
+namespace WebKit {
+class WebDevToolsAgent;
+class WebFrame;
+class WebPluginContainer;
+class WebURLResponse;
+class WebURLLoader;
+class WebURLRequest;
+}
+
+namespace webkit_glue {
+
+class MultipartResponseDelegate;
+class WebPluginDelegate;
+class WebPluginPageDelegate;
+
+// This is the WebKit side of the plugin implementation that forwards calls,
+// after changing out of WebCore types, to a delegate. The delegate may
+// be in a different process.
+class WebPluginImpl : public WebPlugin,
+ public WebKit::WebPlugin,
+ public WebKit::WebURLLoaderClient {
+ public:
+ WebPluginImpl(
+ WebKit::WebFrame* frame,
+ const WebKit::WebPluginParams& params,
+ const FilePath& file_path,
+ const std::string& mime_type,
+ const base::WeakPtr<WebPluginPageDelegate>& page_delegate);
+ virtual ~WebPluginImpl();
+
+ // Helper function for sorting post data.
+ static bool SetPostData(WebKit::WebURLRequest* request,
+ const char* buf,
+ uint32 length);
+
+ virtual WebPluginDelegate* delegate();
+
+ private:
+ // WebKit::WebPlugin methods:
+ virtual bool initialize(
+ WebKit::WebPluginContainer* container);
+ virtual void destroy();
+ virtual NPObject* scriptableObject();
+ virtual void paint(
+ WebKit::WebCanvas* canvas, const WebKit::WebRect& paint_rect);
+ virtual void updateGeometry(
+ const WebKit::WebRect& frame_rect, const WebKit::WebRect& clip_rect,
+ const WebKit::WebVector<WebKit::WebRect>& cut_outs, bool is_visible);
+ virtual unsigned getBackingTextureId();
+ virtual void updateFocus(bool focused);
+ virtual void updateVisibility(bool visible);
+ virtual bool acceptsInputEvents();
+ virtual bool handleInputEvent(
+ const WebKit::WebInputEvent& event, WebKit::WebCursorInfo& cursor_info);
+ virtual void didReceiveResponse(const WebKit::WebURLResponse& response);
+ virtual void didReceiveData(const char* data, int data_length);
+ virtual void didFinishLoading();
+ virtual void didFailLoading(const WebKit::WebURLError& error);
+ virtual void didFinishLoadingFrameRequest(
+ const WebKit::WebURL& url, void* notify_data);
+ virtual void didFailLoadingFrameRequest(
+ const WebKit::WebURL& url, void* notify_data,
+ const WebKit::WebURLError& error);
+ virtual bool supportsPaginatedPrint();
+ virtual int printBegin(const WebKit::WebRect& printable_area,
+ int printer_dpi);
+ virtual bool printPage(int page_number, WebKit::WebCanvas* canvas);
+ virtual void printEnd();
+ virtual bool hasSelection() const;
+ virtual WebKit::WebString selectionAsText() const;
+ virtual WebKit::WebString selectionAsMarkup() const;
+ virtual void setZoomFactor(float scale, bool text_only);
+ virtual bool startFind(const WebKit::WebString& search_text,
+ bool case_sensitive,
+ int identifier);
+ virtual void selectFindResult(bool forward);
+ virtual void stopFind();
+
+ // WebPlugin implementation:
+ virtual void SetWindow(gfx::PluginWindowHandle window);
+ virtual void SetAcceptsInputEvents(bool accepts);
+ virtual void WillDestroyWindow(gfx::PluginWindowHandle window);
+#if defined(OS_WIN)
+ void SetWindowlessPumpEvent(HANDLE pump_messages_event) { }
+#endif
+ virtual void CancelResource(unsigned long id);
+ virtual void Invalidate();
+ virtual void InvalidateRect(const gfx::Rect& rect);
+ virtual NPObject* GetWindowScriptNPObject();
+ virtual NPObject* GetPluginElement();
+ virtual void SetCookie(const GURL& url,
+ const GURL& first_party_for_cookies,
+ const std::string& cookie);
+ virtual std::string GetCookies(const GURL& url,
+ const GURL& first_party_for_cookies);
+ virtual void ShowModalHTMLDialog(const GURL& url, int width, int height,
+ const std::string& json_arguments,
+ std::string* json_retval);
+ virtual void OnMissingPluginStatus(int status);
+
+ virtual void URLRedirectResponse(bool allow, int resource_id);
+
+ // Given a (maybe partial) url, completes using the base url.
+ GURL CompleteURL(const char* url);
+
+ // Executes the script passed in. The notify_needed and notify_data arguments
+ // are passed in by the plugin process. These indicate whether the plugin
+ // expects a notification on script execution. We pass them back to the
+ // plugin as is. This avoids having to track the notification arguments in
+ // the plugin process.
+ bool ExecuteScript(const std::string& url, const std::wstring& script,
+ bool notify_needed, intptr_t notify_data,
+ bool popups_allowed);
+
+ enum RoutingStatus {
+ ROUTED,
+ NOT_ROUTED,
+ INVALID_URL,
+ GENERAL_FAILURE
+ };
+
+ // Determines the referrer value sent along with outgoing HTTP requests
+ // issued by plugins.
+ enum Referrer {
+ PLUGIN_SRC,
+ DOCUMENT_URL,
+ NO_REFERRER
+ };
+
+ // Given a download request, check if we need to route the output to a frame.
+ // Returns ROUTED if the load is done and routed to a frame, NOT_ROUTED or
+ // corresponding error codes otherwise.
+ RoutingStatus RouteToFrame(const char* url,
+ bool is_javascript_url,
+ const char* method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ int notify_id,
+ Referrer referrer_flag);
+
+ // Returns the next avaiable resource id. Returns 0 if the operation fails.
+ // It may fail if the page has already been closed.
+ unsigned long GetNextResourceId();
+
+ // Initiates HTTP GET/POST requests.
+ // Returns true on success.
+ bool InitiateHTTPRequest(unsigned long resource_id,
+ WebPluginResourceClient* client,
+ const GURL& url,
+ const char* method,
+ const char* buf,
+ int len,
+ const char* range_info,
+ Referrer referrer_flag,
+ bool notify_redirects);
+
+ gfx::Rect GetWindowClipRect(const gfx::Rect& rect);
+
+ // Sets the actual Widget for the plugin.
+ void SetContainer(WebKit::WebPluginContainer* container);
+
+ // Destroys the plugin instance.
+ // The response_handle_to_ignore parameter if not NULL indicates the
+ // resource handle to be left valid during plugin shutdown.
+ void TearDownPluginInstance(WebKit::WebURLLoader* loader_to_ignore);
+
+ // WebURLLoaderClient implementation. We implement this interface in the
+ // renderer process, and then use the simple WebPluginResourceClient interface
+ // to relay the callbacks to the plugin.
+ virtual void willSendRequest(WebKit::WebURLLoader* loader,
+ WebKit::WebURLRequest& request,
+ const WebKit::WebURLResponse& response);
+ virtual void didSendData(WebKit::WebURLLoader* loader,
+ unsigned long long bytes_sent,
+ unsigned long long total_bytes_to_be_sent);
+ virtual void didReceiveResponse(WebKit::WebURLLoader* loader,
+ const WebKit::WebURLResponse& response);
+ virtual void didReceiveData(WebKit::WebURLLoader* loader, const char *buffer,
+ int length);
+ virtual void didFinishLoading(WebKit::WebURLLoader* loader,
+ double finishTime);
+ virtual void didFail(WebKit::WebURLLoader* loader,
+ const WebKit::WebURLError& error);
+
+ // Helper function to remove the stored information about a resource
+ // request given its index in m_clients.
+ void RemoveClient(size_t i);
+
+ // Helper function to remove the stored information about a resource
+ // request given a handle.
+ void RemoveClient(WebKit::WebURLLoader* loader);
+
+ virtual void HandleURLRequest(const char* url,
+ const char *method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ int notify_id,
+ bool popups_allowed,
+ bool notify_redirects);
+
+ virtual void CancelDocumentLoad();
+
+ virtual void InitiateHTTPRangeRequest(
+ const char* url, const char* range_info, int pending_request_id);
+
+ virtual void SetDeferResourceLoading(unsigned long resource_id, bool defer);
+
+ // Ignore in-process plugins mode for this flag.
+ virtual bool IsOffTheRecord();
+
+ // Handles HTTP multipart responses, i.e. responses received with a HTTP
+ // status code of 206.
+ void HandleHttpMultipartResponse(const WebKit::WebURLResponse& response,
+ WebPluginResourceClient* client);
+
+ void HandleURLRequestInternal(const char* url,
+ const char* method,
+ const char* target,
+ const char* buf,
+ unsigned int len,
+ int notify_id,
+ bool popups_allowed,
+ Referrer referrer_flag,
+ bool notify_redirects);
+
+ // Tears down the existing plugin instance and creates a new plugin instance
+ // to handle the response identified by the loader parameter.
+ bool ReinitializePluginForResponse(WebKit::WebURLLoader* loader);
+
+ // Delayed task for downloading the plugin source URL.
+ void OnDownloadPluginSrcUrl();
+
+ struct ClientInfo;
+
+ // Helper functions
+ WebPluginResourceClient* GetClientFromLoader(WebKit::WebURLLoader* loader);
+ ClientInfo* GetClientInfoFromLoader(WebKit::WebURLLoader* loader);
+
+ // Helper function to set the referrer on the request passed in.
+ void SetReferrer(WebKit::WebURLRequest* request, Referrer referrer_flag);
+
+ // Returns DevToolsAgent for the frame or 0.
+ WebKit::WebDevToolsAgent* GetDevToolsAgent();
+
+ // Check for invalid chars like @, ;, \ before the first / (in path).
+ bool IsValidUrl(const GURL& url, Referrer referrer_flag);
+
+ std::vector<ClientInfo> clients_;
+
+ bool windowless_;
+ gfx::PluginWindowHandle window_;
+ bool accepts_input_events_;
+ base::WeakPtr<WebPluginPageDelegate> page_delegate_;
+ WebKit::WebFrame* webframe_;
+
+ WebPluginDelegate* delegate_;
+
+ // This is just a weak reference.
+ WebKit::WebPluginContainer* container_;
+
+ typedef std::map<WebPluginResourceClient*,
+ webkit_glue::MultipartResponseDelegate*>
+ MultiPartResponseHandlerMap;
+ // Tracks HTTP multipart response handlers instantiated for
+ // a WebPluginResourceClient instance.
+ MultiPartResponseHandlerMap multi_part_response_map_;
+
+ // The plugin source URL.
+ GURL plugin_url_;
+
+ // Indicates if the download would be initiated by the plugin or us.
+ bool load_manually_;
+
+ // Indicates if this is the first geometry update received by the plugin.
+ bool first_geometry_update_;
+
+ // Set to true if the next response error should be ignored.
+ bool ignore_response_error_;
+
+ // The current plugin geometry and clip rectangle.
+ WebPluginGeometry geometry_;
+
+ // The location of the plugin on disk.
+ FilePath file_path_;
+
+ // The mime type of the plugin.
+ std::string mime_type_;
+
+ // Holds the list of argument names and values passed to the plugin. We keep
+ // these so that we can re-initialize the plugin if we need to.
+ std::vector<std::string> arg_names_;
+ std::vector<std::string> arg_values_;
+
+ ScopedRunnableMethodFactory<WebPluginImpl> method_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebPluginImpl);
+};
+
+} // namespace webkit_glue
+
+#endif // #ifndef WEBKIT_GLUE_WEBPLUGIN_IMPL_H_
diff --git a/webkit/glue/plugins/webplugin_impl_unittest.cc b/webkit/glue/plugins/webplugin_impl_unittest.cc
new file mode 100644
index 0000000..e70e39a
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_impl_unittest.cc
@@ -0,0 +1,232 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h"
+#include "webkit/glue/plugins/webplugin_impl.h"
+
+using WebKit::WebHTTPBody;
+using WebKit::WebString;
+using WebKit::WebURLRequest;
+using webkit_glue::WebPluginImpl;
+
+namespace {
+
+class WebPluginImplTest : public testing::Test {
+};
+
+}
+
+static std::string GetHeader(const WebURLRequest& request, const char* name) {
+ std::string result;
+ TrimWhitespace(
+ request.httpHeaderField(WebString::fromUTF8(name)).utf8(),
+ TRIM_ALL,
+ &result);
+ return result;
+}
+
+static std::string GetBodyText(const WebURLRequest& request) {
+ const WebHTTPBody& body = request.httpBody();
+ if (body.isNull())
+ return std::string();
+
+ std::string result;
+ size_t i = 0;
+ WebHTTPBody::Element element;
+ while (body.elementAt(i++, element)) {
+ if (element.type == WebHTTPBody::Element::TypeData) {
+ result.append(element.data.data(), element.data.size());
+ } else {
+ NOTREACHED() << "unexpected element type encountered!";
+ }
+ }
+ return result;
+}
+
+// The Host functions for NPN_PostURL and NPN_PostURLNotify
+// need to parse out some HTTP headers. Make sure it works
+// with the following tests
+
+TEST(WebPluginImplTest, PostParserSimple) {
+ // Test a simple case with headers & data
+ const char *ex1 = "foo: bar\nContent-length: 10\n\nabcdefghij";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ("bar", GetHeader(request, "foo"));
+ EXPECT_EQ(0U, GetHeader(request, "bar").length());
+ EXPECT_EQ(0U, GetHeader(request, "Content-length").length());
+ EXPECT_EQ("abcdefghij", GetBodyText(request));
+}
+
+TEST(WebPluginImplTest, PostParserLongHeader) {
+ // Test a simple case with long headers
+ const char *ex1 = "foo: 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\n\nabcdefghij";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ(100U, GetHeader(request, "foo").length());
+}
+
+TEST(WebPluginImplTest, PostParserManyHeaders) {
+ // Test a simple case with long headers
+ const char *ex1 = "h1:h1\nh2:h2\nh3:h3\nh4:h4\nh5:h5\nh6:h6\nh7:h7\nh8:h8\nh9:h9\nh10:h10\n\nbody";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ("h1", GetHeader(request, "h1"));
+ EXPECT_EQ("h2", GetHeader(request, "h2"));
+ EXPECT_EQ("h3", GetHeader(request, "h3"));
+ EXPECT_EQ("h4", GetHeader(request, "h4"));
+ EXPECT_EQ("h5", GetHeader(request, "h5"));
+ EXPECT_EQ("h6", GetHeader(request, "h6"));
+ EXPECT_EQ("h7", GetHeader(request, "h7"));
+ EXPECT_EQ("h8", GetHeader(request, "h8"));
+ EXPECT_EQ("h9", GetHeader(request, "h9"));
+ EXPECT_EQ("h10", GetHeader(request, "h10"));
+ EXPECT_EQ("body", GetBodyText(request));
+}
+
+TEST(WebPluginImplTest, PostParserDuplicateHeaders) {
+ // Test a simple case with long headers
+ // What value gets returned doesn't really matter. It shouldn't error
+ // out.
+ const char *ex1 = "h1:h1\nh1:h2\n\nbody";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+}
+
+TEST(WebPluginImplTest, PostParserNoHeaders) {
+ // Test a simple case with no headers but with data
+ const char *ex1 = "\nabcdefghij";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ(0U, GetHeader(request, "foo").length());
+ EXPECT_EQ(0U, GetHeader(request, "bar").length());
+ EXPECT_EQ(0U, GetHeader(request, "Content-length").length());
+ EXPECT_EQ("abcdefghij", GetBodyText(request));
+}
+
+TEST(WebPluginImplTest, PostParserNoBody) {
+ // Test a simple case with headers and no body
+ const char *ex1 = "Foo:bar\n\n";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ("bar", GetHeader(request, "foo"));
+ EXPECT_EQ(0U, GetHeader(request, "bar").length());
+ EXPECT_EQ(0U, GetHeader(request, "Content-length").length());
+ EXPECT_EQ(0U, GetBodyText(request).length());
+}
+
+TEST(WebPluginImplTest, PostParserBodyWithNewLines) {
+ // Test a simple case with headers and no body
+ const char *ex1 = "Foo:bar\n\n\n\nabcdefg\n\nabcdefg";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ(GetBodyText(request), "\n\nabcdefg\n\nabcdefg");
+}
+
+TEST(WebPluginImplTest, PostParserErrorNoBody) {
+ // Test with headers and no body
+ const char *ex1 = "Foo:bar\n";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+}
+
+TEST(WebPluginImplTest, PostParserErrorEmpty) {
+ // Test with an empty string
+ const char *ex1 = "";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+}
+
+TEST(WebPluginImplTest, PostParserEmptyName) {
+ // Test an error case with an empty header name field
+ const char *ex1 = "foo:bar\n:blat\n\nbody";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ("bar", GetHeader(request, "foo"));
+ EXPECT_EQ("body", GetBodyText(request));
+}
+
+TEST(WebPluginImplTest, PostParserEmptyValue) {
+ // Test an error case with an empty value field
+ const char *ex1 = "foo:bar\nbar:\n\nbody";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ("bar", GetHeader(request, "foo"));
+ EXPECT_EQ(0U, GetHeader(request, "bar").length());
+ EXPECT_EQ("body", GetBodyText(request));
+}
+
+TEST(WebPluginImplTest, PostParserCRLF) {
+ // Test an error case with an empty value field
+ const char *ex1 = "foo: bar\r\nbar:\r\n\r\nbody\r\n\r\nbody2";
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ static_cast<uint32>(strlen(ex1)));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ("bar", GetHeader(request, "foo"));
+ EXPECT_EQ(0U, GetHeader(request, "bar").length());
+ EXPECT_EQ("body\r\n\r\nbody2", GetBodyText(request));
+}
+
+TEST(WebPluginImplTest, PostParserBodyWithBinaryData) {
+ // Test a simple case with headers and binary data.
+ char ex1[33] = "foo: bar\nContent-length: 10\n\n";
+ unsigned int binary_data = 0xFFFFFFF0;
+ memcpy(ex1 + strlen("foo: bar\nContent-length: 10\n\n"), &binary_data,
+ sizeof(binary_data));
+
+ WebURLRequest request;
+ request.initialize();
+ bool rv = WebPluginImpl::SetPostData(&request, ex1,
+ sizeof(ex1)/sizeof(ex1[0]));
+ EXPECT_EQ(true, rv);
+ EXPECT_EQ("bar", GetHeader(request, "foo"));
+ EXPECT_EQ(0U, GetHeader(request, "bar").length());
+ EXPECT_EQ(0U, GetHeader(request, "Content-length").length());
+
+ std::string body = GetBodyText(request);
+
+ EXPECT_EQ(0xF0, (unsigned char)body[0]);
+ EXPECT_EQ(0xFF, (unsigned char)body[1]);
+ EXPECT_EQ(0xFF, (unsigned char)body[2]);
+ EXPECT_EQ(0xFF, (unsigned char)body[3]);
+}
diff --git a/webkit/glue/plugins/webplugin_page_delegate.h b/webkit/glue/plugins/webplugin_page_delegate.h
new file mode 100644
index 0000000..d915fdd
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_page_delegate.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBPLUGIN_PAGE_DELEGATE_
+#define WEBKIT_GLUE_WEBPLUGIN_PAGE_DELEGATE_
+
+#include "gfx/native_widget_types.h"
+
+class FilePath;
+class GURL;
+
+namespace WebKit {
+class WebCookieJar;
+}
+
+namespace webkit_glue {
+
+class WebPluginDelegate;
+struct WebPluginGeometry;
+
+// Used by the WebPlugin to communicate back to the containing page.
+class WebPluginPageDelegate {
+ public:
+ // This method is called to create a WebPluginDelegate implementation when a
+ // new plugin is instanced. See webkit_glue::CreateWebPluginDelegateHelper
+ // for a default WebPluginDelegate implementation.
+ virtual WebPluginDelegate* CreatePluginDelegate(
+ const FilePath& file_path,
+ const std::string& mime_type) = 0;
+
+ // Called when a windowed plugin is created.
+ // Lets the view delegate create anything it is using to wrap the plugin.
+ virtual void CreatedPluginWindow(
+ gfx::PluginWindowHandle handle) = 0;
+
+ // Called when a windowed plugin is closing.
+ // Lets the view delegate shut down anything it is using to wrap the plugin.
+ virtual void WillDestroyPluginWindow(
+ gfx::PluginWindowHandle handle) = 0;
+
+ // Keeps track of the necessary window move for a plugin window that resulted
+ // from a scroll operation. That way, all plugin windows can be moved at the
+ // same time as each other and the page.
+ virtual void DidMovePlugin(
+ const WebPluginGeometry& move) = 0;
+
+ // Notifies the parent view that a load has begun.
+ virtual void DidStartLoadingForPlugin() = 0;
+
+ // Notifies the parent view that all loads are finished.
+ virtual void DidStopLoadingForPlugin() = 0;
+
+ // Asks the browser to show a modal HTML dialog. The dialog is passed the
+ // given arguments as a JSON string, and returns its result as a JSON string
+ // through json_retval.
+ virtual void ShowModalHTMLDialogForPlugin(
+ const GURL& url,
+ const gfx::Size& size,
+ const std::string& json_arguments,
+ std::string* json_retval) = 0;
+
+ // The WebCookieJar to use for this plugin.
+ virtual WebKit::WebCookieJar* GetCookieJar() = 0;
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_WEBPLUGIN_PAGE_DELEGATE_H_
diff --git a/webkit/glue/plugins/webplugin_print_delegate.cc b/webkit/glue/plugins/webplugin_print_delegate.cc
new file mode 100644
index 0000000..07bf9b8
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_print_delegate.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webplugin_print_delegate.h"
+
+namespace webkit_glue {
+
+bool WebPluginPrintDelegate::PrintSupportsPrintExtension() {
+ return false;
+}
+
+int WebPluginPrintDelegate::PrintBegin(const gfx::Rect& printable_area,
+ int printer_dpi) {
+ return 0;
+}
+
+bool WebPluginPrintDelegate::PrintPage(int page_number,
+ WebKit::WebCanvas* canvas) {
+ return false;
+}
+
+void WebPluginPrintDelegate::PrintEnd() {
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/plugins/webplugin_print_delegate.h b/webkit/glue/plugins/webplugin_print_delegate.h
new file mode 100644
index 0000000..24298e8
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_print_delegate.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_WEBPLUGIN_PRINT_DELEGATE_H_
+#define WEBKIT_GLUE_PLUGINS_WEBPLUGIN_PRINT_DELEGATE_H_
+
+#include "base/basictypes.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCanvas.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace webkit_glue {
+
+// Interface for the NPAPI print extension. This class implements "NOP"
+// versions of all these functions so it can be used seamlessly by the
+// "regular" plugin delegate while being overridden by the "pepper" one.
+class WebPluginPrintDelegate {
+ public:
+ // If a plugin supports print extensions, then it gets to participate fully
+ // in the browser's print workflow by specifying the number of pages to be
+ // printed and providing a print output for specified pages.
+ virtual bool PrintSupportsPrintExtension();
+
+ // Note: printable_area is in points (a point is 1/72 of an inch).
+ virtual int PrintBegin(const gfx::Rect& printable_area, int printer_dpi);
+
+ virtual bool PrintPage(int page_number, WebKit::WebCanvas* canvas);
+
+ virtual void PrintEnd();
+
+ protected:
+ WebPluginPrintDelegate() {}
+ virtual ~WebPluginPrintDelegate() {}
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_PLUGINS_WEBPLUGIN_PRINT_DELEGATE_H_
+
diff --git a/webkit/glue/plugins/webplugininfo.cc b/webkit/glue/plugins/webplugininfo.cc
new file mode 100644
index 0000000..7d2b4e4
--- /dev/null
+++ b/webkit/glue/plugins/webplugininfo.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webplugininfo.h"
+
+WebPluginMimeType::WebPluginMimeType() {}
+
+WebPluginMimeType::~WebPluginMimeType() {}
+
+WebPluginInfo::WebPluginInfo() : enabled(false) {}
+
+WebPluginInfo::WebPluginInfo(const WebPluginInfo& rhs)
+ : name(rhs.name),
+ path(rhs.path),
+ version(rhs.version),
+ desc(rhs.desc),
+ mime_types(rhs.mime_types),
+ enabled(rhs.enabled) {
+}
+
+WebPluginInfo::~WebPluginInfo() {}
+
+WebPluginInfo& WebPluginInfo::operator=(const WebPluginInfo& rhs) {
+ name = rhs.name;
+ path = rhs.path;
+ version = rhs.version;
+ desc = rhs.desc;
+ mime_types = rhs.mime_types;
+ enabled = rhs.enabled;
+ return *this;
+}
+
+WebPluginInfo::WebPluginInfo(const string16& fake_name,
+ const string16& fake_version,
+ const string16& fake_desc)
+ : name(fake_name),
+ path(),
+ version(fake_version),
+ desc(fake_desc),
+ mime_types(),
+ enabled(true) {
+}
+
diff --git a/webkit/glue/plugins/webplugininfo.h b/webkit/glue/plugins/webplugininfo.h
new file mode 100644
index 0000000..34eff3d
--- /dev/null
+++ b/webkit/glue/plugins/webplugininfo.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_WEBPLUGININFO_H_
+#define WEBKIT_GLUE_WEBPLUGININFO_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+
+// Describes a mime type entry for a plugin.
+struct WebPluginMimeType {
+ WebPluginMimeType();
+ ~WebPluginMimeType();
+
+ // The name of the mime type (e.g., "application/x-shockwave-flash").
+ std::string mime_type;
+
+ // A list of all the file extensions for this mime type.
+ std::vector<std::string> file_extensions;
+
+ // Description of the mime type.
+ string16 description;
+};
+
+// Describes an available NPAPI plugin.
+struct WebPluginInfo {
+ WebPluginInfo();
+ WebPluginInfo(const WebPluginInfo& rhs);
+ ~WebPluginInfo();
+ WebPluginInfo& operator=(const WebPluginInfo& rhs);
+
+ // Special constructor only used during unit testing:
+ WebPluginInfo(const string16& fake_name,
+ const string16& fake_version,
+ const string16& fake_desc);
+
+ // The name of the plugin (i.e. Flash).
+ string16 name;
+
+ // The path to the plugin file (DLL/bundle/library).
+ FilePath path;
+
+ // The version number of the plugin file (may be OS-specific)
+ string16 version;
+
+ // A description of the plugin that we get from its version info.
+ string16 desc;
+
+ // A list of all the mime types that this plugin supports.
+ std::vector<WebPluginMimeType> mime_types;
+
+ // Whether the plugin is enabled.
+ bool enabled;
+};
+
+#endif // WEBKIT_GLUE_WEBPLUGININFO_H_
diff --git a/webkit/glue/plugins/webview_plugin.cc b/webkit/glue/plugins/webview_plugin.cc
new file mode 100644
index 0000000..f89ccb4
--- /dev/null
+++ b/webkit/glue/plugins/webview_plugin.cc
@@ -0,0 +1,235 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webview_plugin.h"
+
+#include "base/message_loop.h"
+#include "base/metrics/histogram.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCursorInfo.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebElement.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPluginContainer.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebSize.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebView.h"
+#include "webkit/glue/webpreferences.h"
+
+#if WEBKIT_USING_CG
+#include <CoreGraphics/CGContext.h>
+#elif WEBKIT_USING_SKIA
+#include "skia/ext/platform_canvas.h"
+#endif
+
+using WebKit::WebCanvas;
+using WebKit::WebCursorInfo;
+using WebKit::WebDragData;
+using WebKit::WebDragOperationsMask;
+using WebKit::WebFrame;
+using WebKit::WebImage;
+using WebKit::WebInputEvent;
+using WebKit::WebMouseEvent;
+using WebKit::WebPlugin;
+using WebKit::WebPluginContainer;
+using WebKit::WebPoint;
+using WebKit::WebRect;
+using WebKit::WebSize;
+using WebKit::WebURLError;
+using WebKit::WebURLRequest;
+using WebKit::WebURLResponse;
+using WebKit::WebVector;
+using WebKit::WebView;
+
+WebViewPlugin::WebViewPlugin(WebViewPlugin::Delegate* delegate)
+ : delegate_(delegate),
+ container_(NULL),
+ finished_loading_(false) {
+ web_view_ = WebView::create(this, NULL);
+ web_view_->initializeMainFrame(this);
+}
+
+// static
+WebViewPlugin* WebViewPlugin::Create(WebViewPlugin::Delegate* delegate,
+ const WebPreferences& preferences,
+ const std::string& html_data,
+ const GURL& url) {
+ WebViewPlugin* plugin = new WebViewPlugin(delegate);
+ WebView* web_view = plugin->web_view();
+ preferences.Apply(web_view);
+ web_view->mainFrame()->loadHTMLString(html_data, url);
+ return plugin;
+}
+
+WebViewPlugin::~WebViewPlugin() {
+ web_view_->close();
+}
+
+void WebViewPlugin::ReplayReceivedData(WebPlugin* plugin) {
+ if (!response_.isNull()) {
+ plugin->didReceiveResponse(response_);
+ size_t total_bytes = 0;
+ for (std::list<std::string>::iterator it = data_.begin();
+ it != data_.end(); ++it) {
+ plugin->didReceiveData(it->c_str(), it->length());
+ total_bytes += it->length();
+ }
+ UMA_HISTOGRAM_MEMORY_KB("PluginDocument.Memory", (total_bytes / 1024));
+ UMA_HISTOGRAM_COUNTS("PluginDocument.NumChunks", data_.size());
+ }
+ if (finished_loading_) {
+ plugin->didFinishLoading();
+ }
+ if (error_.get()) {
+ plugin->didFailLoading(*error_);
+ }
+}
+
+bool WebViewPlugin::initialize(WebPluginContainer* container) {
+ container_ = container;
+ if (container_)
+ old_title_ = container_->element().getAttribute("title");
+ return true;
+}
+
+void WebViewPlugin::destroy() {
+ if (delegate_) {
+ delegate_->WillDestroyPlugin();
+ delegate_ = NULL;
+ }
+ if (container_)
+ container_->element().setAttribute("title", old_title_);
+ container_ = NULL;
+ MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+}
+
+NPObject* WebViewPlugin::scriptableObject() {
+ return NULL;
+}
+
+void WebViewPlugin::paint(WebCanvas* canvas, const WebRect& rect) {
+ gfx::Rect paintRect(rect_.Intersect(rect));
+ if (paintRect.IsEmpty())
+ return;
+
+ paintRect.Offset(-rect_.x(), -rect_.y());
+
+#if WEBKIT_USING_CG
+ CGContextRef context = canvas;
+ CGContextTranslateCTM(context, rect_.x(), rect_.y());
+ CGContextSaveGState(context);
+#elif WEBKIT_USING_SKIA
+ skia::PlatformCanvas* platform_canvas = canvas;
+ platform_canvas->translate(SkIntToScalar(rect_.x()),
+ SkIntToScalar(rect_.y()));
+ platform_canvas->save();
+#endif
+
+ web_view_->layout();
+ web_view_->paint(canvas, paintRect);
+
+#if WEBKIT_USING_SKIA
+ platform_canvas->restore();
+#elif WEBKIT_USING_CG
+ CGContextRestoreGState(context);
+#endif
+}
+
+// Coordinates are relative to the containing window.
+void WebViewPlugin::updateGeometry(
+ const WebRect& frame_rect, const WebRect& clip_rect,
+ const WebVector<WebRect>& cut_out_rects, bool is_visible) {
+ if (frame_rect != rect_) {
+ rect_ = frame_rect;
+ web_view_->resize(WebSize(frame_rect.width, frame_rect.height));
+ }
+}
+
+bool WebViewPlugin::acceptsInputEvents() {
+ return true;
+}
+
+bool WebViewPlugin::handleInputEvent(const WebInputEvent& event,
+ WebCursorInfo& cursor) {
+ if (event.type == WebInputEvent::ContextMenu) {
+ if (delegate_) {
+ const WebMouseEvent& mouse_event =
+ reinterpret_cast<const WebMouseEvent&>(event);
+ delegate_->ShowContextMenu(mouse_event);
+ }
+ return true;
+ }
+ current_cursor_ = cursor;
+ bool handled = web_view_->handleInputEvent(event);
+ cursor = current_cursor_;
+ return handled;
+}
+
+void WebViewPlugin::didReceiveResponse(const WebURLResponse& response) {
+ DCHECK(response_.isNull());
+ response_ = response;
+}
+
+void WebViewPlugin::didReceiveData(const char* data, int data_length) {
+ data_.push_back(std::string(data, data_length));
+}
+
+void WebViewPlugin::didFinishLoading() {
+ DCHECK(!finished_loading_);
+ finished_loading_ = true;
+}
+
+void WebViewPlugin::didFailLoading(const WebURLError& error) {
+ DCHECK(!error_.get());
+ error_.reset(new WebURLError(error));
+}
+
+bool WebViewPlugin::acceptsLoadDrops() {
+ return false;
+}
+
+void WebViewPlugin::setToolTipText(const WebKit::WebString& text,
+ WebKit::WebTextDirection hint) {
+ if (container_)
+ container_->element().setAttribute("title", text);
+}
+
+void WebViewPlugin::startDragging(const WebDragData&,
+ WebDragOperationsMask,
+ const WebImage&,
+ const WebPoint&) {
+ // Immediately stop dragging.
+ web_view_->dragSourceSystemDragEnded();
+}
+
+void WebViewPlugin::didInvalidateRect(const WebRect& rect) {
+ if (container_)
+ container_->invalidateRect(rect);
+}
+
+void WebViewPlugin::didChangeCursor(const WebCursorInfo& cursor) {
+ current_cursor_ = cursor;
+}
+
+void WebViewPlugin::didClearWindowObject(WebFrame* frame) {
+ if (delegate_)
+ delegate_->BindWebFrame(frame);
+}
+
+bool WebViewPlugin::canHandleRequest(WebFrame* frame,
+ const WebURLRequest& request) {
+ return GURL(request.url()).SchemeIs("chrome");
+}
+
+WebURLError WebViewPlugin::cancelledError(WebFrame* frame,
+ const WebURLRequest& request) {
+ // Return an error with a non-zero reason so isNull() on the corresponding
+ // ResourceError is false.
+ WebURLError error;
+ error.domain = "WebViewPlugin";
+ error.reason = -1;
+ error.unreachableURL = request.url();
+ return error;
+}
diff --git a/webkit/glue/plugins/webview_plugin.h b/webkit/glue/plugins/webview_plugin.h
new file mode 100644
index 0000000..f3c75c1
--- /dev/null
+++ b/webkit/glue/plugins/webview_plugin.h
@@ -0,0 +1,142 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_PLUGINS_WEBVIEW_PLUGIN_H_
+#define WEBKIT_GLUE_PLUGINS_WEBVIEW_PLUGIN_H_
+
+#include <list>
+
+#include "base/scoped_ptr.h"
+#include "base/task.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCursorInfo.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrameClient.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebPlugin.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebTextDirection.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebViewClient.h"
+
+namespace WebKit {
+class WebMouseEvent;
+}
+struct WebPreferences;
+
+// This class implements the WebPlugin interface by forwarding drawing and
+// handling input events to a WebView.
+// It can be used as a placeholder for an actual plugin, using HTML for the UI.
+// To show HTML data inside the WebViewPlugin,
+// call web_view->mainFrame()->loadHTMLString() with the HTML data and a fake
+// chrome:// URL as origin.
+
+class WebViewPlugin: public WebKit::WebPlugin, public WebKit::WebViewClient,
+ public WebKit::WebFrameClient {
+ public:
+ class Delegate {
+ public:
+ // Bind |frame| to a Javascript object, enabling the delegate to receive
+ // callback methods from Javascript inside the WebFrame.
+ // This method is called from WebFrameClient::didClearWindowObject.
+ virtual void BindWebFrame(WebKit::WebFrame* frame) = 0;
+
+ // Called before the WebViewPlugin is destroyed. The delegate should delete
+ // itself here.
+ virtual void WillDestroyPlugin() = 0;
+
+ // Called upon a context menu event.
+ virtual void ShowContextMenu(const WebKit::WebMouseEvent&) = 0;
+ };
+
+ explicit WebViewPlugin(Delegate* delegate);
+
+ // Convenience method to set up a new WebViewPlugin using |preferences|
+ // and displaying |html_data|. |url| should be a (fake) chrome:// URL; it is
+ // only used for navigation and never actually resolved.
+ static WebViewPlugin* Create(Delegate* delegate,
+ const WebPreferences& preferences,
+ const std::string& html_data,
+ const GURL& url);
+
+ WebKit::WebView* web_view() { return web_view_; }
+
+ WebKit::WebPluginContainer* container() { return container_; }
+
+ // When loading a plug-in document (i.e. a full page plug-in not embedded in
+ // another page), we save all data that has been received, and replay it with
+ // this method on the actual plug-in.
+ void ReplayReceivedData(WebKit::WebPlugin* plugin);
+
+ // WebPlugin methods:
+ virtual bool initialize(WebKit::WebPluginContainer*);
+ virtual void destroy();
+
+ virtual NPObject* scriptableObject();
+
+ virtual void paint(WebKit::WebCanvas* canvas, const WebKit::WebRect& rect);
+
+ // Coordinates are relative to the containing window.
+ virtual void updateGeometry(
+ const WebKit::WebRect& frame_rect, const WebKit::WebRect& clip_rect,
+ const WebKit::WebVector<WebKit::WebRect>& cut_out_rects, bool is_visible);
+
+ virtual void updateFocus(bool) { }
+ virtual void updateVisibility(bool) { }
+
+ virtual bool acceptsInputEvents();
+ virtual bool handleInputEvent(const WebKit::WebInputEvent& event,
+ WebKit::WebCursorInfo& cursor_info);
+
+ virtual void didReceiveResponse(const WebKit::WebURLResponse& response);
+ virtual void didReceiveData(const char* data, int data_length);
+ virtual void didFinishLoading();
+ virtual void didFailLoading(const WebKit::WebURLError& error);
+
+ // Called in response to WebPluginContainer::loadFrameRequest
+ virtual void didFinishLoadingFrameRequest(
+ const WebKit::WebURL& url, void* notifyData) { }
+ virtual void didFailLoadingFrameRequest(const WebKit::WebURL& url,
+ void* notify_data,
+ const WebKit::WebURLError& error) { }
+
+ // WebViewClient methods:
+ virtual bool acceptsLoadDrops();
+
+ virtual void setToolTipText(const WebKit::WebString&,
+ WebKit::WebTextDirection);
+
+ virtual void startDragging(const WebKit::WebDragData& drag_data,
+ WebKit::WebDragOperationsMask mask,
+ const WebKit::WebImage& image,
+ const WebKit::WebPoint& point);
+
+ // WebWidgetClient methods:
+ virtual void didInvalidateRect(const WebKit::WebRect&);
+ virtual void didChangeCursor(const WebKit::WebCursorInfo& cursor);
+
+ // WebFrameClient methods:
+ virtual void didClearWindowObject(WebKit::WebFrame* frame);
+
+ virtual bool canHandleRequest(WebKit::WebFrame* frame,
+ const WebKit::WebURLRequest& request);
+
+ virtual WebKit::WebURLError cancelledError(
+ WebKit::WebFrame* frame, const WebKit::WebURLRequest& request);
+
+ private:
+ friend class DeleteTask<WebViewPlugin>;
+ virtual ~WebViewPlugin();
+
+ Delegate* delegate_;
+ WebKit::WebCursorInfo current_cursor_;
+ WebKit::WebPluginContainer* container_;
+ WebKit::WebView* web_view_;
+ gfx::Rect rect_;
+
+ WebKit::WebURLResponse response_;
+ std::list<std::string> data_;
+ bool finished_loading_;
+ scoped_ptr<WebKit::WebURLError> error_;
+ WebKit::WebString old_title_;
+};
+
+#endif // WEBKIT_GLUE_PLUGINS_WEBVIEW_PLUGIN_H_