summaryrefslogtreecommitdiffstats
path: root/uriloader/exthandler/nsLocalHandlerApp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'uriloader/exthandler/nsLocalHandlerApp.cpp')
-rw-r--r--uriloader/exthandler/nsLocalHandlerApp.cpp98
1 files changed, 98 insertions, 0 deletions
diff --git a/uriloader/exthandler/nsLocalHandlerApp.cpp b/uriloader/exthandler/nsLocalHandlerApp.cpp
index 6212d2aeb4..fa3f27758f 100644
--- a/uriloader/exthandler/nsLocalHandlerApp.cpp
+++ b/uriloader/exthandler/nsLocalHandlerApp.cpp
@@ -8,11 +8,15 @@
#include "nsIURI.h"
#include "nsIProcess.h"
#include "nsComponentManagerUtils.h"
+#include "mozilla/dom/Promise.h"
+#include "nsProxyRelease.h"
// XXX why does nsMIMEInfoImpl have a threadsafe nsISupports? do we need one
// here too?
NS_IMPL_ISUPPORTS(nsLocalHandlerApp, nsILocalHandlerApp, nsIHandlerApp)
+using namespace mozilla;
+
////////////////////////////////////////////////////////////////////////////////
//// nsIHandlerApp
@@ -28,6 +32,100 @@ NS_IMETHODIMP nsLocalHandlerApp::GetName(nsAString& aName) {
return NS_OK;
}
+/**
+ * This method returns a std::function that will be executed on a thread other
+ * than the main thread. To facilitate things, it should effectively be a global
+ * function that does not maintain a reference to the this pointer. There should
+ * be no reference to any objects that will be shared across threads. Sub-class
+ * implementations should make local copies of everything they need and capture
+ * those in the callback.
+ */
+std::function<nsresult(nsString&)>
+nsLocalHandlerApp::GetPrettyNameOnNonMainThreadCallback() {
+ nsString name;
+
+ // Calculate the name now, on the main thread, so as to avoid
+ // doing anything with the this pointer on the other thread
+ auto result = GetName(name);
+
+ return [name, result](nsString& aName) -> nsresult {
+ aName = name;
+ return result;
+ };
+}
+
+NS_IMETHODIMP
+nsLocalHandlerApp::PrettyNameAsync(JSContext* aCx, dom::Promise** aPromise) {
+ NS_ENSURE_ARG_POINTER(aPromise);
+
+ *aPromise = nullptr;
+
+ if (!mExecutable) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
+ if (NS_WARN_IF(!global)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ ErrorResult err;
+ RefPtr<dom::Promise> outer = dom::Promise::Create(global, err);
+ if (NS_WARN_IF(err.Failed())) {
+ return err.StealNSResult();
+ }
+
+ outer.forget(aPromise);
+
+ nsAutoString executablePath;
+ nsresult result = mExecutable->GetPath(executablePath);
+
+ if (NS_FAILED(result) || executablePath.IsEmpty()) {
+ (*aPromise)->MaybeReject(result);
+ return NS_OK;
+ }
+
+ nsMainThreadPtrHandle<dom::Promise> promiseHolder(
+ new nsMainThreadPtrHolder<dom::Promise>(
+ "nsLocalHandlerApp::prettyExecutableName Promise", *aPromise));
+
+ auto prettyNameGetter = GetPrettyNameOnNonMainThreadCallback();
+
+ result = NS_DispatchBackgroundTask(
+ NS_NewRunnableFunction(
+ __func__,
+ [promiseHolder /* can't move this because if the dispatch fails, we
+ call reject on the promiseHolder */
+ ,
+ prettyNameGetter = std::move(prettyNameGetter)]() mutable -> void {
+ nsAutoString prettyExecutableName;
+
+ nsresult result = prettyNameGetter(prettyExecutableName);
+
+ DebugOnly<nsresult> rv =
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ __func__,
+ [promiseHolder = std::move(promiseHolder),
+ prettyExecutableName = std::move(prettyExecutableName),
+ result]() {
+ if (NS_FAILED(result)) {
+ promiseHolder.get()->MaybeReject(result);
+ } else {
+ promiseHolder.get()->MaybeResolve(prettyExecutableName);
+ }
+ }));
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+ "NS_DispatchToMainThread failed");
+ }),
+ NS_DISPATCH_EVENT_MAY_BLOCK);
+
+ if (NS_FAILED(result)) {
+ promiseHolder.get()->MaybeReject(result);
+ }
+
+ return NS_OK;
+}
+
NS_IMETHODIMP nsLocalHandlerApp::SetName(const nsAString& aName) {
mName.Assign(aName);