|
1 | 1 | #include "Worker.h" |
2 | 2 | #include <pthread.h> |
3 | 3 | #include <functional> |
| 4 | +#include <mutex> |
| 5 | +#include <optional> |
4 | 6 | #include "Caches.h" |
5 | 7 | #include "Constants.h" |
6 | 8 | #include "Helpers.h" |
|
17 | 19 |
|
18 | 20 | std::vector<std::string> Worker::GlobalFunctions = {"postMessage", "close"}; |
19 | 21 |
|
| 22 | +namespace { |
| 23 | + |
| 24 | +// The five names below are the whole public priority surface; anything else is |
| 25 | +// a caller error under `ios.priority` and ignored under the deprecated |
| 26 | +// `iosPriority`. |
| 27 | +bool MapPriorityName(const std::string& name, int& qos) { |
| 28 | + if (name == "userInteractive") { |
| 29 | + qos = NSQualityOfServiceUserInteractive; |
| 30 | + } else if (name == "userInitiated") { |
| 31 | + qos = NSQualityOfServiceUserInitiated; |
| 32 | + } else if (name == "default") { |
| 33 | + qos = NSQualityOfServiceDefault; |
| 34 | + } else if (name == "utility") { |
| 35 | + qos = NSQualityOfServiceUtility; |
| 36 | + } else if (name == "background") { |
| 37 | + qos = NSQualityOfServiceBackground; |
| 38 | + } else { |
| 39 | + return false; |
| 40 | + } |
| 41 | + return true; |
| 42 | +} |
| 43 | + |
| 44 | +// Carries a real TypeError instance so `catch (e) { e instanceof TypeError }` |
| 45 | +// holds in JS; the constructor's catch block rethrows it unchanged. |
| 46 | +[[noreturn]] void ThrowOptionTypeError(Isolate* isolate, const std::string& message) { |
| 47 | + Local<Value> error = Exception::TypeError(tns::ToV8String(isolate, message)); |
| 48 | + throw NativeScriptException(isolate, error, message); |
| 49 | +} |
| 50 | + |
| 51 | +// Reads `key` from `object`. A false return means the getter threw: the |
| 52 | +// exception is already pending on the isolate and construction must stop |
| 53 | +// without running anything else on it. |
| 54 | +bool ReadOption(Isolate* isolate, Local<Context> context, Local<Object> object, const char* key, |
| 55 | + Local<Value>& out) { |
| 56 | + return object->Get(context, tns::ToV8String(isolate, key)).ToLocal(&out); |
| 57 | +} |
| 58 | + |
| 59 | +// Fills `qos` with the quality of service the caller asked for, or leaves it |
| 60 | +// empty for the operation queue's own default. Returns false when a getter |
| 61 | +// threw (see ReadOption). |
| 62 | +bool ParseQualityOfService(Isolate* isolate, Local<Context> context, Local<Object> options, |
| 63 | + std::optional<int>& qos) { |
| 64 | + Local<Value> iosVal; |
| 65 | + if (!ReadOption(isolate, context, options, "ios", iosVal)) { |
| 66 | + return false; |
| 67 | + } |
| 68 | + if (!iosVal->IsNullOrUndefined()) { |
| 69 | + if (!iosVal->IsObject()) { |
| 70 | + ThrowOptionTypeError(isolate, "Worker option \"ios\" must be an object."); |
| 71 | + } |
| 72 | + |
| 73 | + Local<Value> priorityVal; |
| 74 | + if (!ReadOption(isolate, context, iosVal.As<Object>(), "priority", priorityVal)) { |
| 75 | + return false; |
| 76 | + } |
| 77 | + if (!priorityVal->IsUndefined()) { |
| 78 | + int mapped; |
| 79 | + if (!IsString(priorityVal) || !MapPriorityName(ToString(isolate, priorityVal), mapped)) { |
| 80 | + ThrowOptionTypeError(isolate, |
| 81 | + "Worker option \"ios.priority\" must be one of \"userInteractive\", " |
| 82 | + "\"userInitiated\", \"default\", \"utility\" or \"background\"."); |
| 83 | + } |
| 84 | + qos = mapped; |
| 85 | + } |
| 86 | + } |
| 87 | + |
| 88 | + Local<Value> legacyVal; |
| 89 | + if (!ReadOption(isolate, context, options, "iosPriority", legacyVal)) { |
| 90 | + return false; |
| 91 | + } |
| 92 | + if (!legacyVal->IsUndefined()) { |
| 93 | + static std::once_flag warnedDeprecated; |
| 94 | + std::call_once(warnedDeprecated, []() { |
| 95 | + Log(@"NativeScript: the Worker option \"iosPriority\" is deprecated. Use " |
| 96 | + @"\"ios\": { \"priority\": ... } instead."); |
| 97 | + }); |
| 98 | + |
| 99 | + int mapped; |
| 100 | + // Lenient by contract: an unusable legacy value is ignored, never fatal. |
| 101 | + if (!qos.has_value() && IsString(legacyVal) && |
| 102 | + MapPriorityName(ToString(isolate, legacyVal), mapped)) { |
| 103 | + qos = mapped; |
| 104 | + } |
| 105 | + } |
| 106 | + |
| 107 | + return true; |
| 108 | +} |
| 109 | + |
| 110 | +} // namespace |
| 111 | + |
20 | 112 | void Worker::Init(Isolate* isolate, Local<ObjectTemplate> globalTemplate) { |
21 | 113 | Worker::Init(isolate, globalTemplate, Caches::Get(isolate)->isWorker); |
22 | 114 | } |
@@ -148,24 +240,10 @@ throw NativeScriptException( |
148 | 240 | } |
149 | 241 | } |
150 | 242 |
|
151 | | - int qos = -1; |
| 243 | + std::optional<int> qos; |
152 | 244 | if (info.Length() >= 2 && info[1]->IsObject()) { |
153 | | - Local<Object> options = info[1].As<Object>(); |
154 | | - Local<Value> iosPriorityVal; |
155 | | - if (options->Get(context, tns::ToV8String(isolate, "iosPriority")).ToLocal(&iosPriorityVal) && |
156 | | - IsString(iosPriorityVal)) { |
157 | | - std::string priority = ToString(isolate, iosPriorityVal); |
158 | | - if (priority == "userInteractive") { |
159 | | - qos = NSQualityOfServiceUserInteractive; |
160 | | - } else if (priority == "userInitiated") { |
161 | | - qos = NSQualityOfServiceUserInitiated; |
162 | | - } else if (priority == "default") { |
163 | | - qos = NSQualityOfServiceDefault; |
164 | | - } else if (priority == "utility") { |
165 | | - qos = NSQualityOfServiceUtility; |
166 | | - } else if (priority == "background") { |
167 | | - qos = NSQualityOfServiceBackground; |
168 | | - } |
| 245 | + if (!ParseQualityOfService(isolate, context, info[1].As<Object>(), qos)) { |
| 246 | + return; |
169 | 247 | } |
170 | 248 | } |
171 | 249 |
|
|
0 commit comments