| // 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 <map> |
| #include <vector> |
| |
| #include "base/string_util.h" |
| #include "base/utf_string_conversions.h" |
| #include "build/build_config.h" |
| #include "chrome/common/render_messages.h" |
| #include "chrome/renderer/pepper_devices.h" |
| #include "chrome/renderer/webplugin_delegate_pepper.h" |
| #include "chrome/test/render_view_test.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/npapi/bindings/npapi.h" |
| #include "third_party/npapi/bindings/npruntime.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebPlugin.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebPluginParams.h" |
| #include "third_party/WebKit/WebKit/chromium/public/WebRect.h" |
| #include "webkit/glue/plugins/plugin_instance.h" |
| #include "webkit/glue/plugins/plugin_list.h" |
| #include "webkit/glue/plugins/webplugin_impl.h" |
| |
| class PepperDeviceTest; |
| |
| namespace { |
| |
| const char kTestPluginMimeType[] = "chrome-test/pepper-device-test"; |
| |
| // This maps the NPP instances to the test object so our C callbacks can easily |
| // get back to the object. There will normally be only one item in this map. |
| static std::map<NPP, PepperDeviceTest*> active_tests; |
| |
| NPError NPP_New(NPMIMEType plugin_type, NPP instance, |
| uint16 mode, int16 argc, char* argn[], |
| char* argv[], NPSavedData* saved) { |
| // Watch out: active_tests won't contain the NPP pointer until after this |
| // call is complete, so don't use it. |
| return NPERR_NO_ERROR; |
| } |
| |
| NPError NPP_Destroy(NPP instance, NPSavedData** saved) { |
| if (!instance) |
| return NPERR_INVALID_INSTANCE_ERROR; |
| return NPERR_NO_ERROR; |
| } |
| |
| NPError NPP_SetWindow(NPP instance, NPWindow* window) { |
| return NPERR_NO_ERROR; |
| } |
| |
| int16 NPP_HandleEvent(NPP instance, void* event) { |
| return 0; |
| } |
| |
| NPError NPP_GetValue(NPP instance, NPPVariable variable, void* value) { |
| if (!instance) |
| return NPERR_INVALID_INSTANCE_ERROR; |
| switch (variable) { |
| case NPPVpluginNeedsXEmbed: |
| *static_cast<NPBool*>(value) = 1; |
| return NPERR_NO_ERROR; |
| default: |
| return NPERR_INVALID_PARAM; |
| } |
| } |
| |
| NPError NPP_SetValue(NPP instance, NPNVariable variable, void* value) { |
| return NPERR_NO_ERROR; |
| } |
| |
| NPError API_CALL NP_GetEntryPoints(NPPluginFuncs* plugin_funcs) { |
| plugin_funcs->newp = NPP_New; |
| plugin_funcs->destroy = NPP_Destroy; |
| plugin_funcs->setwindow = NPP_SetWindow; |
| plugin_funcs->event = NPP_HandleEvent; |
| plugin_funcs->getvalue = NPP_GetValue; |
| plugin_funcs->setvalue = NPP_SetValue; |
| return NPERR_NO_ERROR; |
| } |
| |
| #if defined(OS_MACOSX) || defined(OS_WIN) |
| NPError API_CALL NP_Initialize(NPNetscapeFuncs* browser_funcs) { |
| return NPERR_NO_ERROR; |
| } |
| #else |
| NPError API_CALL NP_Initialize(NPNetscapeFuncs* browser_funcs, |
| NPPluginFuncs* plugin_funcs) { |
| NP_GetEntryPoints(plugin_funcs); |
| return NPERR_NO_ERROR; |
| } |
| #endif |
| |
| NPError API_CALL NP_Shutdown() { |
| return NPERR_NO_ERROR; |
| } |
| |
| } // namespace |
| |
| // PepperDeviceTest ------------------------------------------------------------ |
| |
| class PepperDeviceTest : public RenderViewTest { |
| public: |
| PepperDeviceTest(); |
| ~PepperDeviceTest(); |
| |
| const FilePath& plugin_path() const { return version_info_.path; } |
| |
| WebPluginDelegatePepper* pepper_plugin() const { return pepper_plugin_; } |
| |
| NPP npp() const { return pepper_plugin_->instance()->npp(); } |
| |
| protected: |
| // Logs that the given flush command was called in flush_calls. |
| static void FlushCalled(NPP instance, |
| NPDeviceContext* context, |
| NPError err, |
| NPUserData* user_data); |
| |
| // Audio callback, currently empty. |
| static void AudioCallback(NPDeviceContextAudio* context); |
| |
| // A log of flush commands we can use to check the async callbacks. |
| struct FlushData { |
| NPP instance; |
| NPDeviceContext* context; |
| NPError err; |
| NPUserData* user_data; |
| }; |
| std::vector<FlushData> flush_calls_; |
| |
| private: |
| // testing::Test implementation. |
| virtual void SetUp(); |
| virtual void TearDown(); |
| |
| NPAPI::PluginVersionInfo version_info_; |
| |
| scoped_ptr<webkit_glue::WebPluginImpl> plugin_; |
| WebPluginDelegatePepper* pepper_plugin_; // FIXME(brettw): check lifetime. |
| }; |
| |
| PepperDeviceTest::PepperDeviceTest() { |
| version_info_.path = FilePath(FILE_PATH_LITERAL("pepper-device-tester")); |
| version_info_.product_name = ASCIIToWide("Pepper device test plugin"); |
| version_info_.file_description = ASCIIToWide("Pepper device test plugin"); |
| version_info_.file_version = ASCIIToWide("1"); |
| version_info_.mime_types = ASCIIToWide(kTestPluginMimeType); |
| NPAPI::PluginEntryPoints entry_points = { |
| #if !defined(OS_POSIX) || defined(OS_MACOSX) |
| NP_GetEntryPoints, |
| #endif |
| NP_Initialize, |
| NP_Shutdown |
| }; |
| version_info_.entry_points = entry_points; |
| } |
| |
| PepperDeviceTest::~PepperDeviceTest() { |
| } |
| |
| void PepperDeviceTest::SetUp() { |
| RenderViewTest::SetUp(); |
| |
| NPAPI::PluginList::Singleton()->RegisterInternalPlugin(version_info_); |
| |
| // Create the WebKit plugin with no delegates (this seems to work |
| // sufficiently for the test). |
| WebKit::WebPluginParams params; |
| plugin_.reset(new webkit_glue::WebPluginImpl( |
| NULL, params, FilePath(), std::string(), |
| base::WeakPtr<webkit_glue::WebPluginPageDelegate>())); |
| |
| // Create a pepper plugin for the RenderView. |
| pepper_plugin_ = WebPluginDelegatePepper::Create( |
| plugin_path(), kTestPluginMimeType, view_->AsWeakPtr()); |
| ASSERT_TRUE(pepper_plugin_); |
| ASSERT_TRUE(pepper_plugin_->Initialize(GURL(), std::vector<std::string>(), |
| std::vector<std::string>(), |
| plugin_.get(), false)); |
| |
| // Normally the RenderView creates the pepper plugin and registers it with |
| // its internal list. Since we're creating it manually, we have to reach in |
| // and register it to prevent tear-down from asserting. |
| view_->current_oldstyle_pepper_plugins_.insert(pepper_plugin_); |
| |
| active_tests[npp()] = this; |
| |
| // Need to specify a window size or graphics calls will fail on the 0x0 |
| // bitmap. |
| gfx::Rect rect(0, 0, 100, 100); |
| view_->OnResize(rect.size(), gfx::Rect()); |
| pepper_plugin_->UpdateGeometry(rect, rect); |
| } |
| |
| void PepperDeviceTest::TearDown() { |
| active_tests.erase(active_tests.find(npp())); |
| |
| plugin_.reset(); |
| if (pepper_plugin_) |
| pepper_plugin_->PluginDestroyed(); |
| |
| NPAPI::PluginList::Singleton()->UnregisterInternalPlugin(version_info_.path); |
| |
| RenderViewTest::TearDown(); |
| } |
| |
| // static |
| void PepperDeviceTest::FlushCalled(NPP instance, |
| NPDeviceContext* context, |
| NPError err, |
| NPUserData* user_data) { |
| if (active_tests.find(instance) == active_tests.end()) |
| return; |
| PepperDeviceTest* that = active_tests[instance]; |
| |
| FlushData flush_data; |
| flush_data.instance = instance; |
| flush_data.context = context; |
| flush_data.err = err; |
| flush_data.user_data = user_data; |
| that->flush_calls_.push_back(flush_data); |
| } |
| |
| void PepperDeviceTest::AudioCallback(NPDeviceContextAudio* context) { |
| } |
| |
| |
| // ----------------------------------------------------------------------------- |
| |
| // TODO(brettw) this crashes on Mac. Figure out why and enable. |
| #if !defined(OS_MACOSX) |
| |
| TEST_F(PepperDeviceTest, Flush) { |
| // Create a 2D device. |
| NPDeviceContext2DConfig config; |
| NPDeviceContext2D context; |
| EXPECT_EQ(NPERR_NO_ERROR, |
| pepper_plugin()->Device2DInitializeContext(&config, &context)); |
| |
| // Flush the bitmap. Here we fake the invalidate call to the RenderView since |
| // there isn't an actual visible web page that would otherwise get painted. |
| // The callback should not get called synchronously. |
| pepper_plugin()->Device2DFlushContext(npp(), &context, &FlushCalled, NULL); |
| view_->didInvalidateRect(WebKit::WebRect(0, 0, 100, 100)); |
| EXPECT_TRUE(flush_calls_.empty()); |
| |
| // Run the message loop which should process the pending paints, there should |
| // still be no callbacks since the stuff hasn't been copied to the screen, |
| // but there should be a paint message sent to the browser. |
| MessageLoop::current()->RunAllPending(); |
| EXPECT_TRUE(flush_calls_.empty()); |
| EXPECT_TRUE(render_thread_.sink().GetFirstMessageMatching( |
| ViewHostMsg_UpdateRect::ID)); |
| |
| // Send a paint ACK, this should trigger the callback. |
| view_->OnMessageReceived(ViewMsg_UpdateRect_ACK(view_->routing_id())); |
| EXPECT_EQ(1u, flush_calls_.size()); |
| } |
| #endif |
| |
| TEST_F(PepperDeviceTest, AudioInit) { |
| NPDeviceContextAudioConfig config; |
| config.sampleRate = NPAudioSampleRate44100Hz; |
| config.sampleType = NPAudioSampleTypeInt16; |
| config.outputChannelMap = NPAudioChannelStereo; |
| config.callback = &AudioCallback; |
| config.userData = this; |
| NPDeviceContextAudio context; |
| EXPECT_EQ(NPERR_NO_ERROR, |
| pepper_plugin()->DeviceAudioInitializeContext(&config, &context)); |
| EXPECT_TRUE(render_thread_.sink().GetFirstMessageMatching( |
| ViewHostMsg_CreateAudioStream::ID)); |
| EXPECT_EQ(NPERR_NO_ERROR, |
| pepper_plugin()->DeviceAudioDestroyContext(&context)); |
| EXPECT_TRUE(render_thread_.sink().GetFirstMessageMatching( |
| ViewHostMsg_CloseAudioStream::ID)); |
| } |
| |