mirror of
https://github.com/status-im/react-native.git
synced 2025-02-06 06:34:01 +00:00
WebWorker: Allow worker script to be loaded from the network in debug builds
Summary:Making buck rebundle the worker script on every JS change is insanely slow. This allows the script to be downloaded for debug builds. The plan is to couple this with an implementation of `require.resolve` which will automatically insert the correct packager network path in DEV builds and the correct local path in release builds. e.g. var worker = new Worker(require.resolve('WebWorkerSample_getPrimesBetween.js')); Reviewed By: lexs Differential Revision: D2939279 fb-gh-sync-id: fbf64bbf1df1649b44e4b98ac504d095c10104a6 shipit-source-id: fbf64bbf1df1649b44e4b98ac504d095c10104a6
This commit is contained in:
parent
d6ded2f807
commit
9199c721de
@ -9,10 +9,20 @@
|
|||||||
|
|
||||||
package com.facebook.react.bridge.webworkers;
|
package com.facebook.react.bridge.webworkers;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import com.facebook.proguard.annotations.DoNotStrip;
|
import com.facebook.proguard.annotations.DoNotStrip;
|
||||||
import com.facebook.react.bridge.queue.MessageQueueThread;
|
import com.facebook.react.bridge.queue.MessageQueueThread;
|
||||||
import com.facebook.react.bridge.queue.MessageQueueThreadImpl;
|
import com.facebook.react.bridge.queue.MessageQueueThreadImpl;
|
||||||
import com.facebook.react.bridge.queue.ProxyQueueThreadExceptionHandler;
|
import com.facebook.react.bridge.queue.ProxyQueueThreadExceptionHandler;
|
||||||
|
import com.facebook.react.common.build.ReactBuildConfig;
|
||||||
|
|
||||||
|
import com.squareup.okhttp.OkHttpClient;
|
||||||
|
import com.squareup.okhttp.Request;
|
||||||
|
import com.squareup.okhttp.Response;
|
||||||
|
import okio.Okio;
|
||||||
|
import okio.Sink;
|
||||||
|
|
||||||
@DoNotStrip
|
@DoNotStrip
|
||||||
public class WebWorkers {
|
public class WebWorkers {
|
||||||
@ -21,9 +31,47 @@ public class WebWorkers {
|
|||||||
* Creates a new MessageQueueThread for a background web worker owned by the JS thread with the
|
* Creates a new MessageQueueThread for a background web worker owned by the JS thread with the
|
||||||
* given MessageQueueThread.
|
* given MessageQueueThread.
|
||||||
*/
|
*/
|
||||||
|
@DoNotStrip
|
||||||
public static MessageQueueThread createWebWorkerThread(int id, MessageQueueThread ownerThread) {
|
public static MessageQueueThread createWebWorkerThread(int id, MessageQueueThread ownerThread) {
|
||||||
return MessageQueueThreadImpl.startNewBackgroundThread(
|
return MessageQueueThreadImpl.startNewBackgroundThread(
|
||||||
"web-worker-" + id,
|
"web-worker-" + id,
|
||||||
new ProxyQueueThreadExceptionHandler(ownerThread));
|
new ProxyQueueThreadExceptionHandler(ownerThread));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility method used to help develop web workers on debug builds. In release builds, worker
|
||||||
|
* scripts need to be packaged with the app, but in dev mode we want to fetch/reload the worker
|
||||||
|
* script on the fly from the packager. This method fetches the given URL *synchronously* and
|
||||||
|
* writes it to the specified temp file.
|
||||||
|
*
|
||||||
|
* This is exposed from Java only because we don't want to add a C++ networking library dependency
|
||||||
|
*
|
||||||
|
* NB: The caller is responsible for deleting the file specified by outFileName when they're done
|
||||||
|
* with it.
|
||||||
|
* NB: We write to a temp file instead of returning a String because, depending on the size of the
|
||||||
|
* worker script, allocating the full script string on the Java heap can cause an OOM.
|
||||||
|
*/
|
||||||
|
@DoNotStrip
|
||||||
|
public static void downloadScriptToFileSync(String url, String outFileName) {
|
||||||
|
if (!ReactBuildConfig.DEBUG) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"For security reasons, downloading scripts is only allowed in debug builds.");
|
||||||
|
}
|
||||||
|
|
||||||
|
OkHttpClient client = new OkHttpClient();
|
||||||
|
final File out = new File(outFileName);
|
||||||
|
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Response response = client.newCall(request).execute();
|
||||||
|
|
||||||
|
Sink output = Okio.sink(out);
|
||||||
|
Okio.buffer(response.body().source()).readAll(output);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Exception downloading web worker script to file", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,8 +118,18 @@ JSCExecutor::JSCExecutor(
|
|||||||
setGlobalVariable(it.first, it.second);
|
setGlobalVariable(it.first, it.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(9604438): Protect against script does not exist
|
// Try to load the script from the network if script is a URL
|
||||||
std::string scriptSrc = WebWorkerUtil::loadScriptFromAssets(script);
|
// NB: For security, this will only work in debug builds
|
||||||
|
std::string scriptSrc;
|
||||||
|
if (script.find("http://") == 0 || script.find("https://") == 0) {
|
||||||
|
std::stringstream outfileBuilder;
|
||||||
|
outfileBuilder << m_deviceCacheDir << "/workerScript" << m_workerId << ".js";
|
||||||
|
scriptSrc = WebWorkerUtil::loadScriptFromNetworkSync(script, outfileBuilder.str());
|
||||||
|
} else {
|
||||||
|
// TODO(9604438): Protect against script does not exist
|
||||||
|
scriptSrc = WebWorkerUtil::loadScriptFromAssets(script);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(9994180): Throw on error
|
// TODO(9994180): Throw on error
|
||||||
loadApplicationScript(scriptSrc, script);
|
loadApplicationScript(scriptSrc, script);
|
||||||
});
|
});
|
||||||
|
@ -16,6 +16,7 @@ GetCurrentMessageQueueThread getCurrentMessageQueueThread;
|
|||||||
namespace WebWorkerUtil {
|
namespace WebWorkerUtil {
|
||||||
WebWorkerQueueFactory createWebWorkerThread;
|
WebWorkerQueueFactory createWebWorkerThread;
|
||||||
LoadScriptFromAssets loadScriptFromAssets;
|
LoadScriptFromAssets loadScriptFromAssets;
|
||||||
|
LoadScriptFromNetworkSync loadScriptFromNetworkSync;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace PerfLogging {
|
namespace PerfLogging {
|
||||||
|
@ -29,6 +29,9 @@ extern WebWorkerQueueFactory createWebWorkerThread;
|
|||||||
|
|
||||||
using LoadScriptFromAssets = std::function<std::string(const std::string& assetName)>;
|
using LoadScriptFromAssets = std::function<std::string(const std::string& assetName)>;
|
||||||
extern LoadScriptFromAssets loadScriptFromAssets;
|
extern LoadScriptFromAssets loadScriptFromAssets;
|
||||||
|
|
||||||
|
using LoadScriptFromNetworkSync = std::function<std::string(const std::string& url, const std::string& tempfileName)>;
|
||||||
|
extern LoadScriptFromNetworkSync loadScriptFromNetworkSync;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace PerfLogging {
|
namespace PerfLogging {
|
||||||
|
@ -914,6 +914,7 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|||||||
[] (const std::string& assetName) {
|
[] (const std::string& assetName) {
|
||||||
return loadScriptFromAssets(assetName);
|
return loadScriptFromAssets(assetName);
|
||||||
};
|
};
|
||||||
|
WebWorkerUtil::loadScriptFromNetworkSync = WebWorkers::loadScriptFromNetworkSync;
|
||||||
MessageQueues::getCurrentMessageQueueThread =
|
MessageQueues::getCurrentMessageQueueThread =
|
||||||
[] {
|
[] {
|
||||||
return std::unique_ptr<MessageQueueThread>(
|
return std::unique_ptr<MessageQueueThread>(
|
||||||
|
@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
#include <folly/Memory.h>
|
#include <folly/Memory.h>
|
||||||
@ -24,6 +27,24 @@ public:
|
|||||||
auto res = method(WebWorkers::javaClassStatic(), id, static_cast<JMessageQueueThread*>(ownerMessageQueueThread)->jobj());
|
auto res = method(WebWorkers::javaClassStatic(), id, static_cast<JMessageQueueThread*>(ownerMessageQueueThread)->jobj());
|
||||||
return folly::make_unique<JMessageQueueThread>(res);
|
return folly::make_unique<JMessageQueueThread>(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string loadScriptFromNetworkSync(const std::string& url, const std::string& tempfileName) {
|
||||||
|
static auto method = WebWorkers::javaClassStatic()->
|
||||||
|
getStaticMethod<void(jstring, jstring)>("downloadScriptToFileSync");
|
||||||
|
method(
|
||||||
|
WebWorkers::javaClassStatic(),
|
||||||
|
jni::make_jstring(url).get(),
|
||||||
|
jni::make_jstring(tempfileName).get());
|
||||||
|
|
||||||
|
std::ifstream tempFile(tempfileName);
|
||||||
|
if (!tempFile.good()) {
|
||||||
|
throw std::runtime_error("Didn't find worker script file at " + tempfileName);
|
||||||
|
}
|
||||||
|
std::stringstream buffer;
|
||||||
|
buffer << tempFile.rdbuf();
|
||||||
|
std::remove(tempfileName.c_str());
|
||||||
|
return buffer.str();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} }
|
} }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user