Index: src/Timeout.cpp |
=================================================================== |
new file mode 100644 |
--- /dev/null |
+++ b/src/Timeout.cpp |
@@ -0,0 +1,120 @@ |
+/* |
+ * This file is part of Adblock Plus <https://adblockplus.org/>, |
+ * Copyright (C) 2006-2017 Eyeo GmbH |
+ * |
+ * Adblock Plus is free software: you can redistribute it and/or modify |
+ * it under the terms of the GNU General Public License version 3 as |
+ * published by the Free Software Foundation. |
+ * |
+ * Adblock Plus is distributed in the hope that it will be useful, |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+ * GNU General Public License for more details. |
+ * |
+ * You should have received a copy of the GNU General Public License |
+ * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
+ */ |
+ |
+#include "JsEngineInternal.h" |
+#include "JsEngineTransition.h" |
+#include "Scheduler.h" |
+#include "Timeout.h" |
+#include "Utils.h" |
+ |
+namespace |
+{ |
+ class TimeoutTask |
+ : public TaskFunctionInterface |
+ { |
+ typedef int64_t delayType; // return type of v8::Value::GetInteger() |
+ /** |
+ * shared_ptr ensures the engine remains in existence |
+ * while we're waiting for the task to complete. |
+ * The scheduler ought to undertake this responsibility, |
+ * but until then we must do it ourselves. |
+ */ |
+ JsEngineInternal* engine; |
+ V8PersistentNG<v8::Function> function; |
+ delayType delay; |
+ PersistentValueArray functionArguments; |
+ DelayedExecutor executor; |
+ |
+ void body() |
+ { |
+ V8ExecutionScope sentry(engine); |
+ auto isolate = engine->GetIsolate(); |
+ engine->ApplyFunction(function.Get(isolate), functionArguments.GetAsLocal(isolate)); |
+ } |
+ |
+ public: |
+ TimeoutTask( |
+ JsEngineInternal *engine, |
+ V8PersistentNG<v8::Function> function, |
+ delayType delay, |
+ PersistentValueArray functionArguments |
+ ) : |
+ engine(engine), |
+ function(function), |
+ delay(delay), |
+ functionArguments(std::move(functionArguments)), |
+ executor([this]() { body(); }) |
+ {} |
+ |
+ void operator()() override |
+ { |
+ executor.RunWithDelay(std::chrono::milliseconds(delay)); |
+ } |
+ |
+ void Interrupt() override |
+ { |
+ executor.ForestallExecution(); |
+ } |
+ }; |
+} |
+ |
+/** |
+ * Callback function registered to v8 to implement `SetTimeout` |
+ * |
+ * Note that this implementation does not support the "evaluate code" version |
+ * of `SetTimeout`, only the "apply function" version. |
+ * |
+ * \par Reference |
+ * https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout |
+ */ |
+v8::Handle<v8::Value> CallbackForSetTimeout(const v8::Arguments& arguments) |
+{ |
+ auto engine = JsEngineInternal::ExtractEngine(arguments); |
+ try |
+ { |
+ if (arguments.Length() < 2) |
+ { |
+ throw std::runtime_error("setTimeout: must have at least 2 arguments"); |
+ } |
+ if (!arguments[0]->IsFunction()) |
+ { |
+ throw std::runtime_error("setTimeout: argument 1 must be a function"); |
+ } |
+ auto isolate = engine->GetIsolate(); |
+ size_t n = arguments.Length() - 2; |
+ PersistentValueArray functionArguments(n); |
+ for (size_t i = 0; i < n; i++) |
+ { |
+ functionArguments[i] = V8PersistentNG<v8::Value>(isolate, arguments[i + 2]); |
+ } |
+ /* |
+ * Run the task |
+ */ |
+ auto task(std::make_shared<TimeoutTask>(engine, |
+ V8PersistentNG<v8::Function>(isolate, v8::Local<v8::Function>::Cast(arguments[0])), |
+ arguments[1]->IntegerValue(), std::move(functionArguments))); |
+ engine->ScheduleTask(std::move(task), ImmediateSingleUseThread); |
+ // We should actually return the timer ID here, which could be |
+ // used via clearTimeout(). But since we don't seem to need |
+ // clearTimeout(), we can save that for later. |
+ return v8::Undefined(); |
+ } |
+ catch (const std::exception& e) |
+ { |
+ return v8::ThrowException(engine->ToV8String(e.what())); |
+ } |
+} |