1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/renderer/browser_plugin/browser_plugin_browsertest.h"
6
7#include "base/debug/leak_annotations.h"
8#include "base/files/file_path.h"
9#include "base/memory/singleton.h"
10#include "base/path_service.h"
11#include "base/pickle.h"
12#include "content/public/common/content_constants.h"
13#include "content/public/renderer/content_renderer_client.h"
14#include "content/renderer/browser_plugin/browser_plugin.h"
15#include "content/renderer/browser_plugin/browser_plugin_manager_factory.h"
16#include "content/renderer/browser_plugin/mock_browser_plugin.h"
17#include "content/renderer/browser_plugin/mock_browser_plugin_manager.h"
18#include "content/renderer/render_thread_impl.h"
19#include "content/renderer/renderer_webkitplatformsupport_impl.h"
20#include "skia/ext/platform_canvas.h"
21#include "third_party/WebKit/public/platform/WebCursorInfo.h"
22#include "third_party/WebKit/public/web/WebInputEvent.h"
23#include "third_party/WebKit/public/web/WebLocalFrame.h"
24#include "third_party/WebKit/public/web/WebScriptSource.h"
25
26namespace content {
27
28namespace {
29const char kHTMLForBrowserPluginObject[] =
30    "<object id='browserplugin' width='640px' height='480px'"
31    " src='foo' type='%s'></object>"
32    "<script>document.querySelector('object').nonExistentAttribute;</script>";
33
34const char kHTMLForSourcelessPluginObject[] =
35    "<object id='browserplugin' width='640px' height='480px' type='%s'>";
36
37std::string GetHTMLForBrowserPluginObject() {
38  return base::StringPrintf(kHTMLForBrowserPluginObject,
39                            kBrowserPluginMimeType);
40}
41
42}  // namespace
43
44// Test factory for creating test instances of BrowserPluginManager.
45class TestBrowserPluginManagerFactory : public BrowserPluginManagerFactory {
46 public:
47  virtual MockBrowserPluginManager* CreateBrowserPluginManager(
48      RenderViewImpl* render_view) OVERRIDE {
49    return new MockBrowserPluginManager(render_view);
50  }
51
52  // Singleton getter.
53  static TestBrowserPluginManagerFactory* GetInstance() {
54    return Singleton<TestBrowserPluginManagerFactory>::get();
55  }
56
57 protected:
58  TestBrowserPluginManagerFactory() {}
59  virtual ~TestBrowserPluginManagerFactory() {}
60
61 private:
62  // For Singleton.
63  friend struct DefaultSingletonTraits<TestBrowserPluginManagerFactory>;
64
65  DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginManagerFactory);
66};
67
68BrowserPluginTest::BrowserPluginTest() {}
69
70BrowserPluginTest::~BrowserPluginTest() {}
71
72void BrowserPluginTest::SetUp() {
73  BrowserPluginManager::set_factory_for_testing(
74      TestBrowserPluginManagerFactory::GetInstance());
75  content::RenderViewTest::SetUp();
76}
77
78void BrowserPluginTest::TearDown() {
79  BrowserPluginManager::set_factory_for_testing(
80      TestBrowserPluginManagerFactory::GetInstance());
81#if defined(LEAK_SANITIZER)
82  // Do this before shutting down V8 in RenderViewTest::TearDown().
83  // http://crbug.com/328552
84  __lsan_do_leak_check();
85#endif
86  RenderViewTest::TearDown();
87}
88
89std::string BrowserPluginTest::ExecuteScriptAndReturnString(
90    const std::string& script) {
91  v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
92  v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue(
93      blink::WebScriptSource(blink::WebString::fromUTF8(script.c_str())));
94  if (value.IsEmpty() || !value->IsString())
95    return std::string();
96
97  v8::Local<v8::String> v8_str = value->ToString();
98  int length = v8_str->Utf8Length() + 1;
99  scoped_ptr<char[]> str(new char[length]);
100  v8_str->WriteUtf8(str.get(), length);
101  return str.get();
102}
103
104int BrowserPluginTest::ExecuteScriptAndReturnInt(
105    const std::string& script) {
106  v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
107  v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue(
108      blink::WebScriptSource(blink::WebString::fromUTF8(script.c_str())));
109  if (value.IsEmpty() || !value->IsInt32())
110    return 0;
111
112  return value->Int32Value();
113}
114
115// A return value of false means that a value was not present. The return value
116// of the script is stored in |result|
117bool BrowserPluginTest::ExecuteScriptAndReturnBool(
118    const std::string& script, bool* result) {
119  v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
120  v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue(
121      blink::WebScriptSource(blink::WebString::fromUTF8(script.c_str())));
122  if (value.IsEmpty() || !value->IsBoolean())
123    return false;
124
125  *result = value->BooleanValue();
126  return true;
127}
128
129MockBrowserPlugin* BrowserPluginTest::GetCurrentPlugin() {
130  BrowserPluginHostMsg_Attach_Params params;
131  return GetCurrentPluginWithAttachParams(&params);
132}
133
134MockBrowserPlugin* BrowserPluginTest::GetCurrentPluginWithAttachParams(
135    BrowserPluginHostMsg_Attach_Params* params) {
136  MockBrowserPlugin* browser_plugin = static_cast<MockBrowserPluginManager*>(
137      browser_plugin_manager())->last_plugin();
138  if (!browser_plugin)
139    return NULL;
140
141  browser_plugin->Attach();
142
143  int instance_id = 0;
144  const IPC::Message* msg =
145      browser_plugin_manager()->sink().GetUniqueMessageMatching(
146          BrowserPluginHostMsg_Attach::ID);
147  if (!msg)
148    return NULL;
149
150  PickleIterator iter(*msg);
151  if (!iter.ReadInt(&instance_id))
152    return NULL;
153
154  if (!IPC::ParamTraits<BrowserPluginHostMsg_Attach_Params>::Read(
155      msg, &iter, params)) {
156    return NULL;
157  }
158
159  browser_plugin->OnAttachACK(instance_id);
160  return browser_plugin;
161}
162
163// This test verifies that an initial resize occurs when we instantiate the
164// browser plugin.
165TEST_F(BrowserPluginTest, InitialResize) {
166  LoadHTML(GetHTMLForBrowserPluginObject().c_str());
167  // Verify that the information in Attach is correct.
168  BrowserPluginHostMsg_Attach_Params params;
169  MockBrowserPlugin* browser_plugin = GetCurrentPluginWithAttachParams(&params);
170
171  EXPECT_EQ(640, params.resize_guest_params.view_size.width());
172  EXPECT_EQ(480, params.resize_guest_params.view_size.height());
173  ASSERT_TRUE(browser_plugin);
174}
175
176TEST_F(BrowserPluginTest, RemovePlugin) {
177  LoadHTML(GetHTMLForBrowserPluginObject().c_str());
178  MockBrowserPlugin* browser_plugin = GetCurrentPlugin();
179  ASSERT_TRUE(browser_plugin);
180
181  EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
182      BrowserPluginHostMsg_PluginDestroyed::ID));
183  ExecuteJavaScript("x = document.getElementById('browserplugin'); "
184                    "x.parentNode.removeChild(x);");
185  ProcessPendingMessages();
186  EXPECT_TRUE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
187      BrowserPluginHostMsg_PluginDestroyed::ID));
188}
189
190// This test verifies that PluginDestroyed messages do not get sent from a
191// BrowserPlugin that has never navigated.
192TEST_F(BrowserPluginTest, RemovePluginBeforeNavigation) {
193  std::string html = base::StringPrintf(kHTMLForSourcelessPluginObject,
194                                        kBrowserPluginMimeType);
195  LoadHTML(html.c_str());
196  EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
197      BrowserPluginHostMsg_PluginDestroyed::ID));
198  ExecuteJavaScript("x = document.getElementById('browserplugin'); "
199                    "x.parentNode.removeChild(x);");
200  ProcessPendingMessages();
201  EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
202      BrowserPluginHostMsg_PluginDestroyed::ID));
203}
204
205}  // namespace content
206