diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 9f63afdf0ef7ad1fe120ca785a77e2cde2335685..437fc0172e0861c95e97aa6794ef2a0b7efdaff8 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -387,6 +387,11 @@ void Engine::RunFromSource(const std::string& main, RunBundleAndSource(bundle_path, main, packages); } +void Engine::SetAssetBundlePath(const std::string& bundle_path) { + TRACE_EVENT0("flutter", "Engine::SetAssetBundlePath"); + ConfigureAssetBundle(bundle_path); +} + Dart_Port Engine::GetUIIsolateMainPort() { if (!runtime_) return ILLEGAL_PORT; diff --git a/shell/common/engine.h b/shell/common/engine.h index 099eaa62e8c3782d1430307f94329cd51fcb7231..b735f3510cc8d88b328751f30c99b68f055fc62e 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -60,6 +60,7 @@ class Engine : public blink::RuntimeDelegate { void RunFromSource(const std::string& main, const std::string& packages, const std::string& bundle); + void SetAssetBundlePath(const std::string& bundle_path); Dart_Port GetUIIsolateMainPort(); std::string GetUIIsolateName(); diff --git a/shell/common/null_platform_view.cc b/shell/common/null_platform_view.cc index 65e61800f21489bbe2ada1f1a97995d59572f492..49fdf5a935b80e7e82a085c49ec7e0145ebc2381 100644 --- a/shell/common/null_platform_view.cc +++ b/shell/common/null_platform_view.cc @@ -31,4 +31,7 @@ void NullPlatformView::RunFromSource(const std::string& assets_directory, const std::string& main, const std::string& packages) {} +void NullPlatformView::SetAssetBundlePath(const std::string& assets_directory) { +} + } // namespace shell diff --git a/shell/common/null_platform_view.h b/shell/common/null_platform_view.h index f740977731ae276f0adeab48ac33b84ee6f5bc38..eb23d67b48a7c554817152e5e31a2dc4e2460aa7 100644 --- a/shell/common/null_platform_view.h +++ b/shell/common/null_platform_view.h @@ -26,6 +26,7 @@ class NullPlatformView : public PlatformView { void RunFromSource(const std::string& assets_directory, const std::string& main, const std::string& packages) override; + void SetAssetBundlePath(const std::string& assets_directory) override; private: fxl::WeakPtrFactory weak_factory_; diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index f572cb2bdfa9a32b868e00e2792711fe01a22145..d8c068c239d2cb9b6dd8ce1acc7d26c0f2303dc7 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -82,6 +82,8 @@ class PlatformView : public std::enable_shared_from_this { const std::string& main, const std::string& packages) = 0; + virtual void SetAssetBundlePath(const std::string& assets_directory) = 0; + protected: explicit PlatformView(std::unique_ptr rasterizer); diff --git a/shell/common/platform_view_service_protocol.cc b/shell/common/platform_view_service_protocol.cc index f29e190ecb8f2c28536355f7abab20432f2cc86d..8785c4812e202c933b449cc35843e07d05aac158 100644 --- a/shell/common/platform_view_service_protocol.cc +++ b/shell/common/platform_view_service_protocol.cc @@ -133,6 +133,8 @@ void PlatformViewServiceProtocol::RegisterHook(bool running_precompiled_code) { } Dart_RegisterRootServiceRequestCallback(kRunInViewExtensionName, &RunInView, nullptr); + Dart_RegisterRootServiceRequestCallback(kSetAssetBundlePathExtensionName, + &SetAssetBundlePath, nullptr); // [benchmark helper] Wait for the UI Thread to idle. Dart_RegisterRootServiceRequestCallback(kFlushUIThreadTasksExtensionName, &FlushUIThreadTasks, nullptr); @@ -188,16 +190,15 @@ bool PlatformViewServiceProtocol::RunInView(const char* method, if (!view_existed) { // If the view did not exist this request has definitely failed. return ErrorUnknownView(json_object, view_id); - } else { - // The view existed and the isolate was created. Success. - std::stringstream response; - response << "{\"type\":\"Success\"," - << "\"view\":"; - AppendFlutterView(&response, view_id_as_num, main_port, isolate_name); - response << "}"; - *json_object = strdup(response.str().c_str()); - return true; } + + // The view existed and the isolate was created. Success. + std::stringstream response; + response << "{\"type\":\"Success\"," + << "\"view\":"; + AppendFlutterView(&response, view_id_as_num, main_port, isolate_name); + response << "}"; + *json_object = strdup(response.str().c_str()); return true; } @@ -391,4 +392,56 @@ bool PlatformViewServiceProtocol::FlushUIThreadTasks(const char* method, return true; } +const char* PlatformViewServiceProtocol::kSetAssetBundlePathExtensionName = + "_flutter.setAssetBundlePath"; + +bool PlatformViewServiceProtocol::SetAssetBundlePath(const char* method, + const char** param_keys, + const char** param_values, + intptr_t num_params, + void* user_data, + const char** json_object) { + const char* view_id = + ValueForKey(param_keys, param_values, num_params, "viewId"); + if (view_id == nullptr) { + return ErrorMissingParameter(json_object, "viewId"); + } + if (strncmp(view_id, kViewIdPrefx, kViewIdPrefxLength) != 0) { + return ErrorBadParameter(json_object, "viewId", view_id); + } + const char* asset_directory = + ValueForKey(param_keys, param_values, num_params, "assetDirectory"); + if (asset_directory == nullptr) { + return ErrorMissingParameter(json_object, "assetDirectory"); + } + + // Convert the actual flutter view hex id into a number. + uintptr_t view_id_as_num = + std::stoull((view_id + kViewIdPrefxLength), nullptr, 16); + + // Ask the Shell to update asset bundle path in the specified view. + // This will run a task on the UI thread before returning. + Shell& shell = Shell::Shared(); + bool view_existed = false; + Dart_Port main_port = ILLEGAL_PORT; + std::string isolate_name; + shell.SetAssetBundlePathInPlatformView(view_id_as_num, asset_directory, + &view_existed, &main_port, + &isolate_name); + + if (!view_existed) { + // If the view did not exist this request has definitely failed. + return ErrorUnknownView(json_object, view_id); + } + + // The view existed and the isolate was created. Success. + std::stringstream response; + response << "{\"type\":\"Success\"," + << "\"view\":"; + AppendFlutterView(&response, view_id_as_num, main_port, isolate_name); + response << "}"; + *json_object = strdup(response.str().c_str()); + return true; +} + } // namespace shell diff --git a/shell/common/platform_view_service_protocol.h b/shell/common/platform_view_service_protocol.h index aedf542c5d03a3059e3e98a28f5a597b2c44ca66..b7f74b56a08ba7f0aaddda8fe6ab24d5d4b258b6 100644 --- a/shell/common/platform_view_service_protocol.h +++ b/shell/common/platform_view_service_protocol.h @@ -70,6 +70,14 @@ class PlatformViewServiceProtocol { intptr_t num_params, void* user_data, const char** json_object); + + static const char* kSetAssetBundlePathExtensionName; + static bool SetAssetBundlePath(const char* method, + const char** param_keys, + const char** param_values, + intptr_t num_params, + void* user_data, + const char** json_object); }; } // namespace shell diff --git a/shell/common/shell.cc b/shell/common/shell.cc index b2b70df5d545062bfeab1a9a7b39052493f9df03..7781df5e81deb325e9653c15b9f02d9e940a8707 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -300,4 +300,60 @@ void Shell::RunInPlatformViewUIThread(uintptr_t view_id, latch->Signal(); } +void Shell::SetAssetBundlePathInPlatformView(uintptr_t view_id, + const char* asset_directory, + bool* view_existed, + int64_t* dart_isolate_id, + std::string* isolate_name) { + fxl::AutoResetWaitableEvent latch; + FXL_DCHECK(view_id != 0); + FXL_DCHECK(asset_directory); + FXL_DCHECK(view_existed); + + blink::Threads::UI()->PostTask([this, view_id, asset_directory, view_existed, + dart_isolate_id, isolate_name, &latch]() { + SetAssetBundlePathInPlatformViewUIThread(view_id, asset_directory, + view_existed, dart_isolate_id, + isolate_name, &latch); + }); + latch.Wait(); +} + +void Shell::SetAssetBundlePathInPlatformViewUIThread( + uintptr_t view_id, + const std::string& assets_directory, + bool* view_existed, + int64_t* dart_isolate_id, + std::string* isolate_name, + fxl::AutoResetWaitableEvent* latch) { + FXL_DCHECK(ui_thread_checker_ && + ui_thread_checker_->IsCreationThreadCurrent()); + + *view_existed = false; + + IteratePlatformViews([ + view_id, // argument + assets_directory = std::move(assets_directory), // argument + &view_existed, // out + &dart_isolate_id, // out + &isolate_name // out + ](PlatformView * view) + ->bool { + if (reinterpret_cast(view) != view_id) { + // Keep looking. + return true; + } + *view_existed = true; + view->SetAssetBundlePath(assets_directory); + *dart_isolate_id = + view->engine().GetUIIsolateMainPort(); + *isolate_name = view->engine().GetUIIsolateName(); + // We found the requested view. Stop iterating over + // platform views. + return false; + }); + + latch->Signal(); +} + } // namespace shell diff --git a/shell/common/shell.h b/shell/common/shell.h index 63d4ac7547d009e8f0a0104ae0a4298635d143e1..96cf3a2fa09312eed3e982a5c54e105c65423bdf 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -54,6 +54,12 @@ class Shell { int64_t* dart_isolate_id, std::string* isolate_name); + void SetAssetBundlePathInPlatformView(uintptr_t view_id, + const char* asset_directory, + bool* view_existed, + int64_t* dart_isolate_id, + std::string* isolate_name); + private: fxl::CommandLine command_line_; std::unique_ptr gpu_thread_; @@ -84,6 +90,14 @@ class Shell { std::string* isolate_name, fxl::AutoResetWaitableEvent* latch); + void SetAssetBundlePathInPlatformViewUIThread( + uintptr_t view_id, + const std::string& main, + bool* view_existed, + int64_t* dart_isolate_id, + std::string* isolate_name, + fxl::AutoResetWaitableEvent* latch); + FXL_DISALLOW_COPY_AND_ASSIGN(Shell); }; diff --git a/shell/platform/android/io/flutter/view/FlutterNativeView.java b/shell/platform/android/io/flutter/view/FlutterNativeView.java index 32673281863005f01708bc210fbe78ab4cab47b3..83421c20b837ccb0e787bb9913caaead53602f71 100644 --- a/shell/platform/android/io/flutter/view/FlutterNativeView.java +++ b/shell/platform/android/io/flutter/view/FlutterNativeView.java @@ -76,6 +76,11 @@ public class FlutterNativeView implements BinaryMessenger { nativeRunBundleAndSource(mNativePlatformView, assetsDirectory, main, packages); } + public void setAssetBundlePathOnUI(final String assetsDirectory) { + assertAttached(); + nativeSetAssetBundlePathOnUI(mNativePlatformView, assetsDirectory); + } + public static String getObservatoryUri() { return nativeGetObservatoryUri(); } @@ -196,6 +201,9 @@ public class FlutterNativeView implements BinaryMessenger { String main, String packages); + private static native void nativeSetAssetBundlePathOnUI(long nativePlatformViewAndroid, + String bundlePath); + private static native String nativeGetObservatoryUri(); // Send an empty platform message to Dart. diff --git a/shell/platform/android/io/flutter/view/FlutterView.java b/shell/platform/android/io/flutter/view/FlutterView.java index 867063f2c09de2b3464f6d7585e9fde1f8bdb138..bcd453bccc05280fb0fc49ec979ee599718c2539 100644 --- a/shell/platform/android/io/flutter/view/FlutterView.java +++ b/shell/platform/android/io/flutter/view/FlutterView.java @@ -647,6 +647,30 @@ public class FlutterView extends SurfaceView } } + private void setAssetBundlePath(final String assetsDirectory) { + Runnable runnable = new Runnable() { + public void run() { + assertAttached(); + mNativeView.setAssetBundlePathOnUI(assetsDirectory); + synchronized (this) { + notify(); + } + } + }; + + try { + synchronized (runnable) { + // Post to the Android UI thread and wait for the response. + post(runnable); + runnable.wait(); + } + } catch (InterruptedException e) { + Log.e(TAG, "Thread got interrupted waiting for " + + "setAssetBundlePath to finish", e); + } + } + + /** * Return the most recent frame as a bitmap. * diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index 8e628c7397af8763bf5df591922a4d188c881817..1dbc2634d2519d0181d3166e00ace555973540dd 100644 --- a/shell/platform/android/platform_view_android.cc +++ b/shell/platform/android/platform_view_android.cc @@ -237,6 +237,14 @@ void PlatformViewAndroid::RunBundleAndSource(std::string bundle_path, }); } +void PlatformViewAndroid::SetAssetBundlePathOnUI(std::string bundle_path) { + blink::Threads::UI()->PostTask( + [ engine = engine_->GetWeakPtr(), bundle_path = std::move(bundle_path) ] { + if (engine) + engine->SetAssetBundlePath(std::move(bundle_path)); + }); +} + void PlatformViewAndroid::SetViewportMetrics(jfloat device_pixel_ratio, jint physical_width, jint physical_height, @@ -570,6 +578,40 @@ void PlatformViewAndroid::RunFromSource(const std::string& assets_directory, fml::jni::DetachFromVM(); } +void PlatformViewAndroid::SetAssetBundlePath( + const std::string& assets_directory) { + JNIEnv* env = fml::jni::AttachCurrentThread(); + FXL_CHECK(env); + + { + fml::jni::ScopedJavaLocalRef local_flutter_view = + flutter_view_.get(env); + if (local_flutter_view.is_null()) { + // Collected. + return; + } + + // Grab the class of the flutter view. + jclass flutter_view_class = env->GetObjectClass(local_flutter_view.obj()); + FXL_CHECK(flutter_view_class); + + // Grab the setAssetBundlePath method id. + jmethodID method_id = env->GetMethodID( + flutter_view_class, "setAssetBundlePathOnUI", "(Ljava/lang/String;)V"); + FXL_CHECK(method_id); + + // Invoke setAssetBundlePath on the Android UI thread. + jstring java_assets_directory = env->NewStringUTF(assets_directory.c_str()); + FXL_CHECK(java_assets_directory); + + env->CallVoidMethod(local_flutter_view.obj(), method_id, + java_assets_directory); + } + + // Detaching from the VM deletes any stray local references. + fml::jni::DetachFromVM(); +} + void PlatformViewAndroid::RegisterExternalTexture( int64_t texture_id, const fml::jni::JavaObjectWeakGlobalRef& surface_texture) { diff --git a/shell/platform/android/platform_view_android.h b/shell/platform/android/platform_view_android.h index 48b26040010931b05f37d56614a10dcb01a40cec..8720976aaa1b263a8a994cad48f09a101af1408d 100644 --- a/shell/platform/android/platform_view_android.h +++ b/shell/platform/android/platform_view_android.h @@ -107,6 +107,10 @@ class PlatformViewAndroid : public PlatformView { const std::string& main, const std::string& packages) override; + void SetAssetBundlePathOnUI(std::string bundle_path); + + void SetAssetBundlePath(const std::string& assets_directory) override; + void RegisterExternalTexture( int64_t texture_id, const fml::jni::JavaObjectWeakGlobalRef& surface_texture); diff --git a/shell/platform/android/platform_view_android_jni.cc b/shell/platform/android/platform_view_android_jni.cc index be70ff4c8bb9ce0fb5f3279bbd931fbc1ecbf53b..80f3d9626086abd3cf6a296adedc48aefe833180 100644 --- a/shell/platform/android/platform_view_android_jni.cc +++ b/shell/platform/android/platform_view_android_jni.cc @@ -181,6 +181,14 @@ void RunBundleAndSource(JNIEnv* env, fml::jni::JavaStringToString(env, packages)); } +void SetAssetBundlePathOnUI(JNIEnv* env, + jobject jcaller, + jlong platform_view, + jstring bundlePath) { + return PLATFORM_VIEW->SetAssetBundlePathOnUI( + fml::jni::JavaStringToString(env, bundlePath)); +} + static void SetViewportMetrics(JNIEnv* env, jobject jcaller, jlong platform_view, @@ -352,6 +360,11 @@ bool PlatformViewAndroid::Register(JNIEnv* env) { "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", .fnPtr = reinterpret_cast(&shell::RunBundleAndSource), }, + { + .name = "nativeSetAssetBundlePathOnUI", + .signature = "(JLjava/lang/String;)V", + .fnPtr = reinterpret_cast(&shell::SetAssetBundlePathOnUI), + }, { .name = "nativeDetach", .signature = "(J)V", diff --git a/shell/platform/darwin/desktop/platform_view_mac.h b/shell/platform/darwin/desktop/platform_view_mac.h index 737f04cd773160bf45be42afcdd2c966587ec43c..501400b6803d46a362444e8b0d002573116dff64 100644 --- a/shell/platform/darwin/desktop/platform_view_mac.h +++ b/shell/platform/darwin/desktop/platform_view_mac.h @@ -41,6 +41,8 @@ class PlatformViewMac : public PlatformView, public GPUSurfaceGLDelegate { const std::string& main, const std::string& packages) override; + void SetAssetBundlePath(const std::string& assets_directory) override; + private: fml::scoped_nsobject opengl_view_; fml::scoped_nsobject resource_loading_context_; @@ -51,6 +53,8 @@ class PlatformViewMac : public PlatformView, public GPUSurfaceGLDelegate { const std::string& main, const std::string& packages); + void SetAssetBundlePathOnUI(const std::string& assets_directory); + FXL_DISALLOW_COPY_AND_ASSIGN(PlatformViewMac); }; diff --git a/shell/platform/darwin/desktop/platform_view_mac.mm b/shell/platform/darwin/desktop/platform_view_mac.mm index 89a1b5c93aed6d9523adf46177384db874d8d14e..42948386fbcd420e80f2b3d1e72dcf7414233185 100644 --- a/shell/platform/darwin/desktop/platform_view_mac.mm +++ b/shell/platform/darwin/desktop/platform_view_mac.mm @@ -73,6 +73,13 @@ void PlatformViewMac::SetupAndLoadFromSource(const std::string& assets_directory }); } +void PlatformViewMac::SetAssetBundlePathOnUI(const std::string& assets_directory) { + blink::Threads::UI()->PostTask([ engine = engine().GetWeakPtr(), assets_directory ] { + if (engine) + engine->SetAssetBundlePath(assets_directory); + }); +} + intptr_t PlatformViewMac::GLContextFBO() const { // Default window bound framebuffer FBO 0. return 0; @@ -153,4 +160,16 @@ void PlatformViewMac::RunFromSource(const std::string& assets_directory, delete latch; } +void PlatformViewMac::SetAssetBundlePath(const std::string& assets_directory) { + auto latch = new fxl::ManualResetWaitableEvent(); + + dispatch_async(dispatch_get_main_queue(), ^{ + SetAssetBundlePathOnUI(assets_directory); + latch->Signal(); + }); + + latch->Wait(); + delete latch; +} + } // namespace shell diff --git a/shell/platform/darwin/ios/platform_view_ios.h b/shell/platform/darwin/ios/platform_view_ios.h index d13371d0db9b595841a5fbb25174be5e13bf5aff..d2daade5b1057298e1c937efe9c129dce74cbd52 100644 --- a/shell/platform/darwin/ios/platform_view_ios.h +++ b/shell/platform/darwin/ios/platform_view_ios.h @@ -59,6 +59,8 @@ class PlatformViewIOS : public PlatformView { const std::string& main, const std::string& packages) override; + void SetAssetBundlePath(const std::string& assets_directory) override; + NSObject* binary_messenger() const { return binary_messenger_; } @@ -75,6 +77,8 @@ class PlatformViewIOS : public PlatformView { const std::string& main, const std::string& packages); + void SetAssetBundlePathOnUI(const std::string& assets_directory); + FXL_DISALLOW_COPY_AND_ASSIGN(PlatformViewIOS); }; diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index caefbed22e2316e658c1cd8f5efdb84e1355c5e6..d763d0715eb7dfa301f97a5ab224128a049ff0fe 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -69,6 +69,13 @@ void PlatformViewIOS::SetupAndLoadFromSource(const std::string& assets_directory }); } +void PlatformViewIOS::SetAssetBundlePathOnUI(const std::string& assets_directory) { + blink::Threads::UI()->PostTask([ engine = engine().GetWeakPtr(), assets_directory ] { + if (engine) + engine->SetAssetBundlePath(assets_directory); + }); +} + fml::WeakPtr PlatformViewIOS::GetWeakPtr() { return weak_factory_.GetWeakPtr(); } @@ -120,4 +127,16 @@ void PlatformViewIOS::RunFromSource(const std::string& assets_directory, delete latch; } +void PlatformViewIOS::SetAssetBundlePath(const std::string& assets_directory) { + auto latch = new fxl::ManualResetWaitableEvent(); + + dispatch_async(dispatch_get_main_queue(), ^{ + SetAssetBundlePathOnUI(assets_directory); + latch->Signal(); + }); + + latch->Wait(); + delete latch; +} + } // namespace shell diff --git a/shell/platform/embedder/platform_view_embedder.cc b/shell/platform/embedder/platform_view_embedder.cc index c2cbba72431abfe9508b5d287e4355095e2c86ef..37e6c033241de46ea7adc5c76c5f1a977542ed27 100644 --- a/shell/platform/embedder/platform_view_embedder.cc +++ b/shell/platform/embedder/platform_view_embedder.cc @@ -47,6 +47,11 @@ void PlatformViewEmbedder::RunFromSource(const std::string& assets_directory, FXL_LOG(INFO) << "Hot reloading is unsupported on this platform."; } +void PlatformViewEmbedder::SetAssetBundlePath( + const std::string& assets_directory) { + FXL_LOG(INFO) << "Set asset bundle path is unsupported on this platform."; +} + void PlatformViewEmbedder::HandlePlatformMessage( fxl::RefPtr message) { if (!message) { diff --git a/shell/platform/embedder/platform_view_embedder.h b/shell/platform/embedder/platform_view_embedder.h index 7f3a4cdd979e16e1631be813a93f41472d4ad591..31779e4e972bdbfc6eaafb4d0f1a7b439edc920c 100644 --- a/shell/platform/embedder/platform_view_embedder.h +++ b/shell/platform/embedder/platform_view_embedder.h @@ -52,6 +52,9 @@ class PlatformViewEmbedder : public PlatformView, public GPUSurfaceGLDelegate { const std::string& main, const std::string& packages) override; + // |shell::PlatformView| + void SetAssetBundlePath(const std::string& assets_directory) override; + // |shell::PlatformView| void HandlePlatformMessage( fxl::RefPtr message) override; diff --git a/shell/testing/platform_view_test.cc b/shell/testing/platform_view_test.cc index 5b21939093c5a321f63b3c212a7db335ce2c8a86..a59684d31bc9448ce9548df621a923314ef693d5 100644 --- a/shell/testing/platform_view_test.cc +++ b/shell/testing/platform_view_test.cc @@ -26,4 +26,7 @@ void PlatformViewTest::RunFromSource(const std::string& assets_directory, const std::string& main, const std::string& packages) {} +void PlatformViewTest::SetAssetBundlePath(const std::string& assets_directory) { +} + } // namespace shell diff --git a/shell/testing/platform_view_test.h b/shell/testing/platform_view_test.h index ea4d60ef5fdfa7e1bf6282a7cc915cd7e4206ebf..3612d0363ab58a26a4317bbc8275228cb613dd1f 100644 --- a/shell/testing/platform_view_test.h +++ b/shell/testing/platform_view_test.h @@ -27,6 +27,8 @@ class PlatformViewTest : public PlatformView { const std::string& main, const std::string& packages) override; + void SetAssetBundlePath(const std::string& assets_directory) override; + private: FXL_DISALLOW_COPY_AND_ASSIGN(PlatformViewTest); };