summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/test/data/npapi/schedule_timer.html25
-rw-r--r--chrome/test/ui/npapi_uitest.cc7
-rw-r--r--third_party/npapi/bindings/npapi.h2
-rw-r--r--webkit/glue/plugins/nphostapi.h8
-rw-r--r--webkit/glue/plugins/plugin_host.cc18
-rw-r--r--webkit/glue/plugins/plugin_instance.cc87
-rw-r--r--webkit/glue/plugins/plugin_instance.h25
-rw-r--r--webkit/glue/plugins/test/plugin_client.cc4
-rw-r--r--webkit/glue/plugins/test/plugin_schedule_timer_test.cc116
-rw-r--r--webkit/glue/plugins/test/plugin_schedule_timer_test.h70
-rw-r--r--webkit/tools/test_shell/test_shell.gyp8
11 files changed, 351 insertions, 19 deletions
diff --git a/chrome/test/data/npapi/schedule_timer.html b/chrome/test/data/npapi/schedule_timer.html
new file mode 100644
index 0000000..2546bb9
--- /dev/null
+++ b/chrome/test/data/npapi/schedule_timer.html
@@ -0,0 +1,25 @@
+<html>
+
+<head>
+<script src="npapi.js"></script>
+</head>
+
+
+<body>
+<div id="statusPanel" style="border: 1px solid red; width: 100%">
+Test running....
+</div>
+
+
+NPAPI NPN_ScheduleTimer test<p>
+Tests that a plugin can schedule and unschedule timers.<P>
+
+<embed type="application/vnd.npapi-test"
+ src="foo"
+ name="schedule_timer"
+ id="1"
+ mode="np_embed"
+>
+
+</body>
+</html>
diff --git a/chrome/test/ui/npapi_uitest.cc b/chrome/test/ui/npapi_uitest.cc
index 4e8b26f..206fdd2 100644
--- a/chrome/test/ui/npapi_uitest.cc
+++ b/chrome/test/ui/npapi_uitest.cc
@@ -293,6 +293,13 @@ TEST_F(NPAPITester, PrivateDisabled) {
kTestCompleteSuccess, kShortWaitTimeout);
}
+TEST_F(NPAPITester, ScheduleTimer) {
+ GURL url = GetTestUrl(L"npapi", L"schedule_timer.html");
+ NavigateToURL(url);
+ WaitForFinish("schedule_timer", "1", url, kTestCompleteCookie,
+ kTestCompleteSuccess, kShortWaitTimeout);
+}
+
// Test checking the privacy mode is on.
TEST_F(NPAPIIncognitoTester, PrivateEnabled) {
if (UITest::in_process_renderer())
diff --git a/third_party/npapi/bindings/npapi.h b/third_party/npapi/bindings/npapi.h
index 47b4744..a7e6a3b 100644
--- a/third_party/npapi/bindings/npapi.h
+++ b/third_party/npapi/bindings/npapi.h
@@ -139,7 +139,7 @@
#define NP_VERSION_MAJOR 0
// BEGIN GOOGLE MODIFICATIONS
-#define NP_VERSION_MINOR 22 // maximum version currently supported by Chromium
+#define NP_VERSION_MINOR 23 // maximum version currently supported by Chromium
// END GOOGLE MODIFICATIONS
diff --git a/webkit/glue/plugins/nphostapi.h b/webkit/glue/plugins/nphostapi.h
index d2ec615..8463838c 100644
--- a/webkit/glue/plugins/nphostapi.h
+++ b/webkit/glue/plugins/nphostapi.h
@@ -206,6 +206,12 @@ typedef NPError (*NPN_GetAuthenticationInfoPtr)(NPP npp,
uint32_t *ulen,
char **password,
uint32_t *plen);
+typedef uint32 (*NPN_ScheduleTimerPtr)(NPP npp,
+ uint32 interval,
+ NPBool repeat,
+ void (*timerFunc)(NPP npp, uint32 timerID));
+typedef void (*NPN_UnscheduleTimerPtr)(NPP npp,
+ uint32 timerID);
//
// NPAPI Function table of NPP functions (functions provided by plugin to host)
@@ -284,6 +290,8 @@ typedef struct _NPNetscapeFuncs {
NPN_GetValueForURLPtr getvalueforurl;
NPN_SetValueForURLPtr setvalueforurl;
NPN_GetAuthenticationInfoPtr getauthenticationinfo;
+ NPN_ScheduleTimerPtr scheduletimer;
+ NPN_UnscheduleTimerPtr unscheduletimer;
} NPNetscapeFuncs;
//
diff --git a/webkit/glue/plugins/plugin_host.cc b/webkit/glue/plugins/plugin_host.cc
index c21dd07..ddbae1c 100644
--- a/webkit/glue/plugins/plugin_host.cc
+++ b/webkit/glue/plugins/plugin_host.cc
@@ -108,6 +108,8 @@ void PluginHost::InitializeHostFuncs() {
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;
}
void PluginHost::PatchNPNetscapeFuncs(NPNetscapeFuncs* overrides) {
@@ -1090,4 +1092,20 @@ NPError NPN_GetAuthenticationInfo(NPP id,
return NPERR_GENERIC_ERROR;
}
+uint32 NPN_ScheduleTimer(NPP id,
+ uint32 interval,
+ NPBool repeat,
+ void (*func)(NPP id, uint32 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 timer_id) {
+ scoped_refptr<NPAPI::PluginInstance> plugin = FindInstance(id);
+ if (plugin)
+ plugin->UnscheduleTimer(timer_id);
+}
} // extern "C"
diff --git a/webkit/glue/plugins/plugin_instance.cc b/webkit/glue/plugins/plugin_instance.cc
index 7f71f8e..8ae1aeb 100644
--- a/webkit/glue/plugins/plugin_instance.cc
+++ b/webkit/glue/plugins/plugin_instance.cc
@@ -38,7 +38,8 @@ PluginInstance::PluginInstance(PluginLib *plugin, const std::string &mime_type)
#endif
message_loop_(MessageLoop::current()),
load_manually_(false),
- in_close_streams_(false) {
+ in_close_streams_(false),
+ next_timer_id_(1) {
npp_ = new NPP_t();
npp_->ndata = 0;
npp_->pdata = 0;
@@ -156,7 +157,7 @@ NPError PluginInstance::NPP_New(unsigned short mode,
void PluginInstance::NPP_Destroy() {
DCHECK(npp_functions_ != 0);
- DCHECK(npp_functions_->newp != 0);
+ DCHECK(npp_functions_->destroy != 0);
if (npp_functions_->destroy != 0) {
NPSavedData *savedData = 0;
@@ -175,6 +176,9 @@ void PluginInstance::NPP_Destroy() {
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) {
@@ -361,19 +365,74 @@ void PluginInstance::PluginThreadAsyncCall(void (*func)(void *),
void PluginInstance::OnPluginThreadAsyncCall(void (*func)(void *),
void *user_data) {
-#if defined(OS_WIN)
- // We are invoking an arbitrary callback provided by a third
- // party plugin. It's better to wrap this into an exception
- // block to protect us from crashes.
- __try {
- func(user_data);
- } __except(EXCEPTION_EXECUTE_HANDLER) {
- // Maybe we can disable a crashing plugin.
- // But for now, just continue.
- }
-#else
func(user_data);
-#endif
+}
+
+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;
+ timers_[timer_id] = info;
+
+ // Schedule the callback.
+ message_loop_->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);
+}
+
+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) {
+ message_loop_->PostDelayedTask(FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &PluginInstance::OnTimerCall,
+ func,
+ npp_,
+ timer_id),
+ info.interval);
+ } else {
+ timers_.erase(it);
+ }
}
void PluginInstance::PushPopupsEnabledState(bool enabled) {
diff --git a/webkit/glue/plugins/plugin_instance.h b/webkit/glue/plugins/plugin_instance.h
index aea05ab..385acf5 100644
--- a/webkit/glue/plugins/plugin_instance.h
+++ b/webkit/glue/plugins/plugin_instance.h
@@ -8,9 +8,11 @@
#ifndef WEBKIT_GLUE_PLUGIN_PLUGIN_INSTANCE_H__
#define WEBKIT_GLUE_PLUGIN_PLUGIN_INSTANCE_H__
+#include <map>
+#include <set>
+#include <stack>
#include <string>
#include <vector>
-#include <stack>
#include "app/gfx/native_widget_types.h"
#include "base/basictypes.h"
@@ -150,6 +152,12 @@ class PluginInstance : public base::RefCountedThreadSafe<PluginInstance> {
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);
+
//
// NPAPI methods for calling the Plugin Instance
//
@@ -193,6 +201,10 @@ class PluginInstance : public base::RefCountedThreadSafe<PluginInstance> {
private:
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);
// This is a hack to get the real player plugin to work with chrome
@@ -251,6 +263,17 @@ class PluginInstance : public base::RefCountedThreadSafe<PluginInstance> {
// 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_;
+
DISALLOW_EVIL_CONSTRUCTORS(PluginInstance);
};
diff --git a/webkit/glue/plugins/test/plugin_client.cc b/webkit/glue/plugins/test/plugin_client.cc
index 1146a6a..3bde2c1 100644
--- a/webkit/glue/plugins/test/plugin_client.cc
+++ b/webkit/glue/plugins/test/plugin_client.cc
@@ -16,6 +16,7 @@
#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_private_test.h"
+#include "webkit/glue/plugins/test/plugin_schedule_timer_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_window_size_test.h"
@@ -171,6 +172,9 @@ NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode,
} else if (test_name == "private") {
new_test = new NPAPIClient::PrivateTest(instance,
NPAPIClient::PluginClient::HostFunctions());
+ } else if (test_name == "schedule_timer") {
+ new_test = new NPAPIClient::ScheduleTimerTest(
+ instance, NPAPIClient::PluginClient::HostFunctions());
#if defined(OS_WIN)
// TODO(port): plugin_windowed_test.*.
} else if (test_name == "hidden_plugin" ||
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..9ca947f
--- /dev/null
+++ b/webkit/glue/plugins/test/plugin_schedule_timer_test.h
@@ -0,0 +1,70 @@
+// 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_SCHEDULE_TIMER_TEST_H
+#define WEBKIT_GLUE_PLUGINS_TEST_PLUGIN_SCHEDULE_TIMER_TEST_H
+
+#include <vector>
+
+#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/tools/test_shell/test_shell.gyp b/webkit/tools/test_shell/test_shell.gyp
index 9e2087c..6870e10 100644
--- a/webkit/tools/test_shell/test_shell.gyp
+++ b/webkit/tools/test_shell/test_shell.gyp
@@ -170,7 +170,7 @@
'../../../breakpad/breakpad.gyp:breakpad_handler',
'../../default_plugin/default_plugin.gyp:default_plugin',
],
- # TODO(bradnelson):
+ # TODO(bradnelson):
# This should really be done in the 'npapi_layout_test_plugin'
# target, but the current VS generator handles 'copies'
# settings as AdditionalDependencies, which means that
@@ -570,9 +570,11 @@
'../../glue/plugins/test/plugin_npobject_lifetime_test.cc',
'../../glue/plugins/test/plugin_npobject_lifetime_test.h',
'../../glue/plugins/test/plugin_npobject_proxy_test.cc',
+ '../../glue/plugins/test/plugin_npobject_proxy_test.h',
+ '../../glue/plugins/test/plugin_schedule_timer_test.cc',
+ '../../glue/plugins/test/plugin_schedule_timer_test.h',
'../../glue/plugins/test/plugin_windowed_test.cc',
'../../glue/plugins/test/plugin_windowed_test.h',
- '../../glue/plugins/test/plugin_npobject_proxy_test.h',
'../../glue/plugins/test/plugin_private_test.cc',
'../../glue/plugins/test/plugin_private_test.h',
'../../glue/plugins/test/plugin_test.cc',
@@ -659,7 +661,7 @@
['OS=="win"', {
'targets': [
{
- # Helper application that disables ClearType during the
+ # Helper application that disables ClearType during the
# running of the layout tests
'target_name': 'layout_test_helper',
'type': 'executable',