diff --git a/analysis_options.yaml b/analysis_options.yaml index 5ceb890d98720..6420a5457adca 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -42,7 +42,7 @@ linter: # - avoid_bool_literals_in_conditional_expressions # not yet tested # - avoid_catches_without_on_clauses # we do this commonly # - avoid_catching_errors # we do this commonly - - avoid_classes_with_only_static_members + - avoid_classes_with_only_static_members # We want to avoid classes that can be instantiated but only have statics - avoid_empty_else - avoid_function_literals_in_foreach_calls - avoid_init_to_null diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index 8526070f9b1e2..f58ed6d102802 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -52,6 +52,8 @@ source_set("ui") { "painting/shader.h", "painting/vertices.cc", "painting/vertices.h", + "plugins/callback_cache.cc", + "plugins/callback_cache.h", "semantics/semantics_node.cc", "semantics/semantics_node.h", "semantics/semantics_update.cc", diff --git a/lib/ui/dart_runtime_hooks.cc b/lib/ui/dart_runtime_hooks.cc index ca129cb4d9768..e9b3cb32399f5 100644 --- a/lib/ui/dart_runtime_hooks.cc +++ b/lib/ui/dart_runtime_hooks.cc @@ -12,6 +12,7 @@ #include #include "flutter/common/settings.h" +#include "flutter/lib/ui/plugins/callback_cache.h" #include "flutter/lib/ui/ui_dart_state.h" #include "lib/fxl/build_config.h" #include "lib/fxl/logging.h" @@ -36,6 +37,7 @@ extern void syslog(int, const char*, ...); } #endif +using tonic::DartConverter; using tonic::LogIfError; using tonic::ToDart; @@ -48,7 +50,9 @@ namespace blink { #define BUILTIN_NATIVE_LIST(V) \ V(Logger_PrintString, 1) \ V(SaveCompilationTrace, 0) \ - V(ScheduleMicrotask, 1) + V(ScheduleMicrotask, 1) \ + V(GetCallbackHandle, 1) \ + V(GetCallbackFromHandle, 1) BUILTIN_NATIVE_LIST(DECLARE_FUNCTION); @@ -56,7 +60,7 @@ void DartRuntimeHooks::RegisterNatives(tonic::DartLibraryNatives* natives) { natives->Register({BUILTIN_NATIVE_LIST(REGISTER_FUNCTION)}); } -static Dart_Handle GetClosure(Dart_Handle builtin_library, const char* name) { +static Dart_Handle GetFunction(Dart_Handle builtin_library, const char* name) { Dart_Handle getter_name = ToDart(name); Dart_Handle closure = Dart_Invoke(builtin_library, getter_name, 0, nullptr); DART_CHECK_VALID(closure); @@ -64,7 +68,7 @@ static Dart_Handle GetClosure(Dart_Handle builtin_library, const char* name) { } static void InitDartInternal(Dart_Handle builtin_library, bool is_ui_isolate) { - Dart_Handle print = GetClosure(builtin_library, "_getPrintClosure"); + Dart_Handle print = GetFunction(builtin_library, "_getPrintClosure"); Dart_Handle internal_library = Dart_LookupLibrary(ToDart("dart:_internal")); @@ -101,7 +105,7 @@ static void InitDartAsync(Dart_Handle builtin_library, bool is_ui_isolate) { Dart_Handle schedule_microtask; if (is_ui_isolate) { schedule_microtask = - GetClosure(builtin_library, "_getScheduleMicrotaskClosure"); + GetFunction(builtin_library, "_getScheduleMicrotaskClosure"); } else { Dart_Handle isolate_lib = Dart_LookupLibrary(ToDart("dart:isolate")); Dart_Handle method_name = @@ -125,7 +129,8 @@ static void InitDartIO(Dart_Handle builtin_library, DART_CHECK_VALID(Dart_SetField(platform_type, ToDart("_nativeScript"), ToDart(script_uri))); } - Dart_Handle locale_closure = GetClosure(builtin_library, "_getLocaleClosure"); + Dart_Handle locale_closure = + GetFunction(builtin_library, "_getLocaleClosure"); DART_CHECK_VALID( Dart_SetField(platform_type, ToDart("_localeClosure"), locale_closure)); } @@ -223,4 +228,103 @@ void ScheduleMicrotask(Dart_NativeArguments args) { UIDartState::Current()->ScheduleMicrotask(closure); } +static std::string GetFunctionLibraryUrl(Dart_Handle closure) { + if (Dart_IsClosure(closure)) { + closure = Dart_ClosureFunction(closure); + DART_CHECK_VALID(closure); + } + + if (!Dart_IsFunction(closure)) { + return ""; + } + + Dart_Handle url = Dart_Null(); + Dart_Handle owner = Dart_FunctionOwner(closure); + if (Dart_IsInstance(owner)) { + owner = Dart_ClassLibrary(owner); + } + if (Dart_IsLibrary(owner)) { + url = Dart_LibraryUrl(owner); + DART_CHECK_VALID(url); + } + return DartConverter::FromDart(url); +} + +static std::string GetFunctionClassName(Dart_Handle closure) { + Dart_Handle result; + + if (Dart_IsClosure(closure)) { + closure = Dart_ClosureFunction(closure); + DART_CHECK_VALID(closure); + } + + if (!Dart_IsFunction(closure)) { + return ""; + } + + bool is_static = false; + result = Dart_FunctionIsStatic(closure, &is_static); + DART_CHECK_VALID(result); + if (!is_static) { + return ""; + } + + result = Dart_FunctionOwner(closure); + DART_CHECK_VALID(result); + + if (Dart_IsLibrary(result) || !Dart_IsInstance(result)) { + return ""; + } + return DartConverter::FromDart(Dart_ClassName(result)); +} + +static std::string GetFunctionName(Dart_Handle func) { + DART_CHECK_VALID(func); + + if (Dart_IsClosure(func)) { + func = Dart_ClosureFunction(func); + DART_CHECK_VALID(func); + } + + if (!Dart_IsFunction(func)) { + return ""; + } + + bool is_static = false; + Dart_Handle result = Dart_FunctionIsStatic(func, &is_static); + DART_CHECK_VALID(result); + if (!is_static) { + return ""; + } + + result = Dart_FunctionName(func); + if (Dart_IsError(result)) { + Dart_PropagateError(result); + return ""; + } + + return DartConverter::FromDart(result); +} + +void GetCallbackHandle(Dart_NativeArguments args) { + Dart_Handle func = Dart_GetNativeArgument(args, 0); + std::string name = GetFunctionName(func); + std::string class_name = GetFunctionClassName(func); + std::string library_path = GetFunctionLibraryUrl(func); + + if (name.empty()) { + Dart_SetReturnValue(args, Dart_Null()); + return; + } + Dart_SetReturnValue( + args, DartConverter::ToDart(DartCallbackCache::GetCallbackHandle( + name, class_name, library_path))); +} + +void GetCallbackFromHandle(Dart_NativeArguments args) { + Dart_Handle h = Dart_GetNativeArgument(args, 0); + int64_t handle = DartConverter::FromDart(h); + Dart_SetReturnValue(args, DartCallbackCache::GetCallback(handle)); +} + } // namespace blink diff --git a/lib/ui/dart_ui.gni b/lib/ui/dart_ui.gni index e1885d1d09ace..e9a315ac3694e 100644 --- a/lib/ui/dart_ui.gni +++ b/lib/ui/dart_ui.gni @@ -11,6 +11,7 @@ dart_ui_files = [ "$flutter_root/lib/ui/lerp.dart", "$flutter_root/lib/ui/natives.dart", "$flutter_root/lib/ui/painting.dart", + "$flutter_root/lib/ui/plugins.dart", "$flutter_root/lib/ui/pointer.dart", "$flutter_root/lib/ui/semantics.dart", "$flutter_root/lib/ui/text.dart", diff --git a/lib/ui/natives.dart b/lib/ui/natives.dart index 4dfb2e481675e..c31ecb0b6f51a 100644 --- a/lib/ui/natives.dart +++ b/lib/ui/natives.dart @@ -48,6 +48,9 @@ dynamic _saveCompilationTrace() native 'SaveCompilationTrace'; void _scheduleMicrotask(void callback()) native 'ScheduleMicrotask'; +int _getCallbackHandle(Function closure) native 'GetCallbackHandle'; +Function _getCallbackFromHandle(int handle) native 'GetCallbackFromHandle'; + // Required for gen_snapshot to work correctly. int _isolateId; diff --git a/lib/ui/plugins.dart b/lib/ui/plugins.dart new file mode 100644 index 0000000000000..215c52124763b --- /dev/null +++ b/lib/ui/plugins.dart @@ -0,0 +1,69 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +part of dart.ui; + +/// An wrapper for a raw callback handle. +class CallbackHandle { + final int _handle; + + /// Create an instance using a raw callback handle. + /// + /// Only values produced by a call to [CallbackHandle.toRawHandle] should be + /// used, otherwise this object will be an invalid handle. + CallbackHandle.fromRawHandle(this._handle) + : assert(_handle != null, "'_handle' must not be null."); + + /// Get the raw callback handle to pass over a [MethodChannel] or isolate + /// port. + int toRawHandle() => _handle; + + @override + int get hashCode => _handle; + + @override + bool operator ==(dynamic other) => + (other is CallbackHandle) && (_handle == other._handle); +} + +/// Functionality for Flutter plugin authors. +abstract class PluginUtilities { + // This class is only a namespace, and should not be instantiated or + // extended directly. + factory PluginUtilities._() => null; + + static Map _forwardCache = + {}; + static Map _backwardCache = + {}; + + /// Get a handle to a named top-level or static callback function which can + /// be easily passed between isolates. + /// + /// `callback` must not be null. + /// + /// Returns a [CallbackHandle] that can be provided to + /// [PluginUtilities.getCallbackFromHandle] to retrieve a tear-off of the + /// original callback. If `callback` is not a top-level or static function, + /// null is returned. + static CallbackHandle getCallbackHandle(Function callback) { + assert(callback != null, "'callback' must not be null."); + return _forwardCache.putIfAbsent(callback, + () => new CallbackHandle.fromRawHandle(_getCallbackHandle(callback))); + } + + /// Get a tear-off of a named top-level or static callback represented by a + /// handle. + /// + /// `handle` must not be null. + /// + /// If `handle` is not a valid handle returned by + /// [PluginUtilities.getCallbackHandle], null is returned. Otherwise, a + /// tear-off of the callback associated with `handle` is returned. + static Function getCallbackFromHandle(CallbackHandle handle) { + assert(handle != null, "'handle' must not be null."); + return _backwardCache.putIfAbsent( + handle, () => _getCallbackFromHandle(handle.toRawHandle())); + } +} diff --git a/lib/ui/plugins/callback_cache.cc b/lib/ui/plugins/callback_cache.cc new file mode 100644 index 0000000000000..ad31891a2f90c --- /dev/null +++ b/lib/ui/plugins/callback_cache.cc @@ -0,0 +1,87 @@ +// Copyright 2018 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/lib/ui/plugins/callback_cache.h" +#include "lib/fxl/logging.h" +#include "lib/tonic/converter/dart_converter.h" + +using tonic::ToDart; + +namespace blink { + +std::mutex DartCallbackCache::mutex_; +std::map DartCallbackCache::cache_; + +Dart_Handle DartCallbackCache::GetCallback(int64_t handle) { + std::unique_lock lock(mutex_); + auto iterator = cache_.find(handle); + if (iterator != cache_.end()) { + DartCallbackRepresentation cb = iterator->second; + return LookupDartClosure(cb.name, cb.class_name, cb.library_path); + } + return Dart_Null(); +} + +int64_t DartCallbackCache::GetCallbackHandle(const std::string& name, + const std::string& class_name, + const std::string& library_path) { + std::unique_lock lock(mutex_); + std::hash hasher; + int64_t hash = hasher(name); + hash += hasher(class_name); + hash += hasher(library_path); + + if (cache_.find(hash) == cache_.end()) { + cache_[hash] = {name, class_name, library_path}; + } + return hash; +} + +std::unique_ptr +DartCallbackCache::GetCallbackInformation(int64_t handle) { + std::unique_lock lock(mutex_); + auto iterator = cache_.find(handle); + if (iterator != cache_.end()) { + return std::make_unique(iterator->second); + } + return nullptr; +} + +Dart_Handle DartCallbackCache::LookupDartClosure( + const std::string& name, + const std::string& class_name, + const std::string& library_path) { + Dart_Handle closure_name = ToDart(name); + Dart_Handle library_name = + library_path.empty() ? Dart_Null() : ToDart(library_path); + Dart_Handle cls_name = class_name.empty() ? Dart_Null() : ToDart(class_name); + DART_CHECK_VALID(closure_name); + DART_CHECK_VALID(library_name); + DART_CHECK_VALID(cls_name); + + Dart_Handle library; + if (library_name == Dart_Null()) { + library = Dart_RootLibrary(); + } else { + library = Dart_LookupLibrary(library_name); + } + DART_CHECK_VALID(library); + + Dart_Handle closure; + if (Dart_IsNull(cls_name)) { + closure = Dart_GetClosure(library, closure_name); + } else { + Dart_Handle cls = Dart_GetClass(library, cls_name); + DART_CHECK_VALID(cls); + if (Dart_IsNull(cls)) { + closure = Dart_Null(); + } else { + closure = Dart_GetStaticMethodClosure(library, cls, closure_name); + } + } + DART_CHECK_VALID(closure); + return closure; +} + +} // namespace blink diff --git a/lib/ui/plugins/callback_cache.h b/lib/ui/plugins/callback_cache.h new file mode 100644 index 0000000000000..a32dfcbbcf02c --- /dev/null +++ b/lib/ui/plugins/callback_cache.h @@ -0,0 +1,55 @@ +// Copyright 2018 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_LIB_UI_CALLBACK_CACHE_H_ +#define FLUTTER_LIB_UI_CALLBACK_CACHE_H_ + +#include +#include +#include +#include + +#include "flutter/fml/synchronization/thread_annotations.h" +#include "lib/fxl/macros.h" +#include "third_party/dart/runtime/include/dart_api.h" + +#define DART_CALLBACK_INVALID_HANDLE -1 +#define LOCK_UNLOCK(m) FML_ACQUIRE(m) FML_RELEASE(m) + +namespace blink { + +typedef struct { + std::string name; + std::string class_name; + std::string library_path; +} DartCallbackRepresentation; + +class DartCallbackCache { + public: + static int64_t GetCallbackHandle(const std::string& name, + const std::string& class_name, + const std::string& library_path) + LOCK_UNLOCK(mutex_); + + static Dart_Handle GetCallback(int64_t handle) LOCK_UNLOCK(mutex_); + + static std::unique_ptr GetCallbackInformation( + int64_t handle) LOCK_UNLOCK(mutex_); + + private: + static Dart_Handle LookupDartClosure(const std::string& name, + const std::string& class_name, + const std::string& library_path); + + static std::mutex mutex_; + + static std::map cache_ + FML_GUARDED_BY(mutex_); + + FXL_DISALLOW_IMPLICIT_CONSTRUCTORS(DartCallbackCache); +}; + +} // namespace blink + +#endif // FLUTTER_LIB_UI_CALLBACK_CACHE_H_ diff --git a/lib/ui/ui.dart b/lib/ui/ui.dart index 1d9782311f310..8c4480c072cdb 100644 --- a/lib/ui/ui.dart +++ b/lib/ui/ui.dart @@ -30,6 +30,7 @@ part 'isolate_name_server.dart'; part 'lerp.dart'; part 'natives.dart'; part 'painting.dart'; +part 'plugins.dart'; part 'pointer.dart'; part 'semantics.dart'; part 'text.dart'; diff --git a/runtime/dart_isolate.cc b/runtime/dart_isolate.cc index 8774a827f9842..d1ecaf0191d9e 100644 --- a/runtime/dart_isolate.cc +++ b/runtime/dart_isolate.cc @@ -487,6 +487,48 @@ bool DartIsolate::Run(const std::string& entrypoint_name) { return true; } +FXL_WARN_UNUSED_RESULT +bool DartIsolate::RunFromLibrary(const std::string& library_name, + const std::string& entrypoint_name) { + TRACE_EVENT0("flutter", "DartIsolate::RunFromLibrary"); + if (phase_ != Phase::Ready) { + return false; + } + + tonic::DartState::Scope scope(this); + + Dart_Handle library = Dart_LookupLibrary(tonic::ToDart(library_name.c_str())); + if (tonic::LogIfError(library)) { + return false; + } + + Dart_Handle entrypoint = + Dart_GetClosure(library, tonic::ToDart(entrypoint_name.c_str())); + if (tonic::LogIfError(entrypoint)) { + return false; + } + + Dart_Handle isolate_lib = Dart_LookupLibrary(tonic::ToDart("dart:isolate")); + if (tonic::LogIfError(isolate_lib)) { + return false; + } + + Dart_Handle isolate_args[] = { + entrypoint, + Dart_Null(), + }; + + if (tonic::LogIfError(Dart_Invoke( + isolate_lib, tonic::ToDart("_startMainIsolate"), + sizeof(isolate_args) / sizeof(isolate_args[0]), isolate_args))) { + return false; + } + + phase_ = Phase::Running; + FXL_DLOG(INFO) << "New isolate is in the running state."; + return true; +} + bool DartIsolate::Shutdown() { TRACE_EVENT0("flutter", "DartIsolate::Shutdown"); // This call may be re-entrant since Dart_ShutdownIsolate can invoke the diff --git a/runtime/dart_isolate.h b/runtime/dart_isolate.h index fe928f40794ab..6ed4b04d415d2 100644 --- a/runtime/dart_isolate.h +++ b/runtime/dart_isolate.h @@ -79,6 +79,10 @@ class DartIsolate : public UIDartState { FXL_WARN_UNUSED_RESULT bool Run(const std::string& entrypoint); + FXL_WARN_UNUSED_RESULT + bool RunFromLibrary(const std::string& library_name, + const std::string& entrypoint); + FXL_WARN_UNUSED_RESULT bool Shutdown(); diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 60453633b126c..3f8896a224d70 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -152,9 +152,17 @@ bool Engine::PrepareAndLaunchIsolate(RunConfiguration configuration) { return false; } - if (!isolate->Run(configuration.GetEntrypoint())) { - FXL_LOG(ERROR) << "Could not run the isolate."; - return false; + if (configuration.GetEntrypointLibrary().empty()) { + if (!isolate->Run(configuration.GetEntrypoint())) { + FXL_LOG(ERROR) << "Could not run the isolate."; + return false; + } + } else { + if (!isolate->RunFromLibrary(configuration.GetEntrypointLibrary(), + configuration.GetEntrypoint())) { + FXL_LOG(ERROR) << "Could not run the isolate."; + return false; + } } return true; diff --git a/shell/common/run_configuration.cc b/shell/common/run_configuration.cc index eb315f25e8280..1fba1da6b9d6d 100644 --- a/shell/common/run_configuration.cc +++ b/shell/common/run_configuration.cc @@ -60,6 +60,12 @@ void RunConfiguration::SetEntrypoint(std::string entrypoint) { entrypoint_ = std::move(entrypoint); } +void RunConfiguration::SetEntrypointAndLibrary(std::string entrypoint, + std::string library) { + SetEntrypoint(entrypoint); + entrypoint_library_ = std::move(library); +} + fml::RefPtr RunConfiguration::GetAssetManager() const { return asset_manager_; } @@ -68,6 +74,10 @@ const std::string& RunConfiguration::GetEntrypoint() const { return entrypoint_; } +const std::string& RunConfiguration::GetEntrypointLibrary() const { + return entrypoint_library_; +} + std::unique_ptr RunConfiguration::TakeIsolateConfiguration() { return std::move(isolate_configuration_); diff --git a/shell/common/run_configuration.h b/shell/common/run_configuration.h index 5d45d2a26bc09..b0adf9181fa6d 100644 --- a/shell/common/run_configuration.h +++ b/shell/common/run_configuration.h @@ -37,16 +37,21 @@ class RunConfiguration { void SetEntrypoint(std::string entrypoint); + void SetEntrypointAndLibrary(std::string entrypoint, std::string library); + fml::RefPtr GetAssetManager() const; const std::string& GetEntrypoint() const; + const std::string& GetEntrypointLibrary() const; + std::unique_ptr TakeIsolateConfiguration(); private: std::unique_ptr isolate_configuration_; fml::RefPtr asset_manager_; std::string entrypoint_ = "main"; + std::string entrypoint_library_ = ""; FXL_DISALLOW_COPY_AND_ASSIGN(RunConfiguration); }; diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index d9450d64d65f4..8e0fb443f5f27 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -22,6 +22,7 @@ _flutter_framework_headers = [ "framework/Headers/Flutter.h", "framework/Headers/FlutterAppDelegate.h", "framework/Headers/FlutterBinaryMessenger.h", + "framework/Headers/FlutterCallbackCache.h", "framework/Headers/FlutterChannels.h", "framework/Headers/FlutterCodecs.h", "framework/Headers/FlutterDartProject.h", @@ -43,6 +44,8 @@ shared_library("create_flutter_framework_dylib") { sources = [ "framework/Source/FlutterAppDelegate.mm", + "framework/Source/FlutterAppDelegate_Internal.h", + "framework/Source/FlutterCallbackCache.mm", "framework/Source/FlutterChannels.mm", "framework/Source/FlutterCodecs.mm", "framework/Source/FlutterDartProject.mm", @@ -73,6 +76,8 @@ shared_library("create_flutter_framework_dylib") { "framework/Source/platform_message_router.mm", "framework/Source/vsync_waiter_ios.h", "framework/Source/vsync_waiter_ios.mm", + "headless_platform_view_ios.h", + "headless_platform_view_ios.mm", "ios_external_texture_gl.h", "ios_external_texture_gl.mm", "ios_gl_context.h", diff --git a/shell/platform/darwin/ios/framework/Headers/Flutter.h b/shell/platform/darwin/ios/framework/Headers/Flutter.h index d850854f2f517..ddadf8431368a 100644 --- a/shell/platform/darwin/ios/framework/Headers/Flutter.h +++ b/shell/platform/darwin/ios/framework/Headers/Flutter.h @@ -36,6 +36,7 @@ #include "FlutterAppDelegate.h" #include "FlutterBinaryMessenger.h" +#include "FlutterCallbackCache.h" #include "FlutterChannels.h" #include "FlutterCodecs.h" #include "FlutterDartProject.h" diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h b/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h new file mode 100644 index 0000000000000..7150838fafbb3 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h @@ -0,0 +1,37 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FLUTTERCALLBACKCACHE_H_ +#define FLUTTER_FLUTTERCALLBACKCACHE_H_ + +#import + +#include "FlutterMacros.h" + +FLUTTER_EXPORT +@interface FlutterCallbackInformation : NSObject +@property(retain) NSString* callbackName; +@property(retain) NSString* callbackClassName; +@property(retain) NSString* callbackLibraryPath; +@end + +FLUTTER_EXPORT +@interface FlutterCallbackCache : NSObject +/** + Returns the callback information for the given callback handle. + This callback information can be used when spawning a + FlutterHeadlessDartRunner. + + - Parameter handle: The handle for a callback, provided by the + Dart method `PluginUtilities.getCallbackHandle`. + - Returns: A FlutterCallbackInformation object which contains the name of the + callback, the name of the class in which the callback is defined, and the + path of the library which contains the callback. If the provided handle is + invalid, nil is returned. + */ ++ (FlutterCallbackInformation*)lookupCallbackInformation:(int64_t)handle; + +@end + +#endif // FLUTTER_FLUTTERCALLBACKCACHE_H_ diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h b/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h index 522766e410355..646a5164c43d9 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h @@ -7,28 +7,51 @@ #import +#include "FlutterBinaryMessenger.h" #include "FlutterDartProject.h" #include "FlutterMacros.h" +/** +A callback for when FlutterHeadlessDartRunner has attempted to start a Dart +Isolate in the background. + +- Parameter success: YES if the Isolate was started and run successfully, NO + otherwise. +*/ +typedef void (^FlutterHeadlessDartRunnerCallback)(BOOL success); + /** The FlutterHeadlessDartRunner runs Flutter Dart code with a null rasterizer, and no native drawing surface. It is appropriate for use in running Dart code e.g. in the background from a plugin. */ FLUTTER_EXPORT -@interface FlutterHeadlessDartRunner : NSObject +@interface FlutterHeadlessDartRunner : NSObject /** Runs a Dart function on an Isolate that is not the main application's Isolate. - The first call will create a new Isolate. Subsequent calls will reuse that - Isolate. The Isolate is destroyed when the FlutterHeadlessDartRunner is - destroyed. + The first call will create a new Isolate. Subsequent calls will return + immediately. - Parameter entrypoint: The name of a top-level function from the same Dart library that contains the app's main() function. */ - (void)runWithEntrypoint:(NSString*)entrypoint; +/** + Runs a Dart function on an Isolate that is not the main application's Isolate. + The first call will create a new Isolate. Subsequent calls will return + immediately. + + - Parameter entrypoint: The name of a top-level function from a Dart library. + - Parameter uri: The URI of the Dart library which contains entrypoint. + - Parameter callback: The callback to be invoked when the new Isolate is + invoked. +*/ +- (void)runWithEntrypointAndCallback:(NSString*)entrypoint + libraryUri:(NSString*)uri + completion:(FlutterHeadlessDartRunnerCallback)callback; + @end #endif // FLUTTER_FLUTTERHEADLESSDARTRUNNER_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Internal.h new file mode 100644 index 0000000000000..c01a33d6f3266 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Internal.h @@ -0,0 +1,18 @@ +// Copyright 2018 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterAppDelegate.h" + +@interface FlutterAppDelegate () + +@property(readonly, nonatomic) NSMutableArray* pluginDelegates; +@property(readonly, nonatomic) NSMutableDictionary* pluginPublications; + +@end + +@interface FlutterAppDelegateRegistrar : NSObject + +- (instancetype)initWithPlugin:(NSString*)pluginKey appDelegate:(FlutterAppDelegate*)delegate; + +@end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache.mm b/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache.mm new file mode 100644 index 0000000000000..61ff0b165d385 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache.mm @@ -0,0 +1,26 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h" + +#include "flutter/lib/ui/plugins/callback_cache.h" + +@implementation FlutterCallbackInformation +@end + +@implementation FlutterCallbackCache + ++ (FlutterCallbackInformation*)lookupCallbackInformation:(int64_t)handle { + auto info = blink::DartCallbackCache::GetCallbackInformation(handle); + if (info == nullptr) { + return nil; + } + FlutterCallbackInformation* new_info = [[FlutterCallbackInformation alloc] init]; + new_info.callbackName = [NSString stringWithUTF8String:info->name.c_str()]; + new_info.callbackClassName = [NSString stringWithUTF8String:info->class_name.c_str()]; + new_info.callbackLibraryPath = [NSString stringWithUTF8String:info->library_path.c_str()]; + return new_info; +} + +@end \ No newline at end of file diff --git a/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm b/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm index add5a78dc4a4c..0a422e2f77b06 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterHeadlessDartRunner.mm @@ -8,6 +8,7 @@ #include #include +#include #include "flutter/fml/message_loop.h" #include "flutter/shell/common/engine.h" @@ -17,29 +18,44 @@ #include "flutter/shell/common/switches.h" #include "flutter/shell/common/thread_host.h" #include "flutter/shell/platform/darwin/common/command_line.h" +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h" +#include "flutter/shell/platform/darwin/ios/headless_platform_view_ios.h" +#include "flutter/shell/platform/darwin/ios/platform_view_ios.h" #include "lib/fxl/functional/make_copyable.h" -static std::unique_ptr CreateHeadlessPlatformView(shell::Shell& shell) { - return std::make_unique(shell, shell.GetTaskRunners()); +static std::unique_ptr CreateHeadlessPlatformView( + shell::Shell& shell) { + return std::make_unique(shell, shell.GetTaskRunners()); } static std::unique_ptr CreateHeadlessRasterizer(shell::Shell& shell) { return std::make_unique(shell.GetTaskRunners()); } +static std::string CreateShellLabel() { + static size_t count = 1; + std::stringstream stream; + stream << "io.flutter.headless."; + stream << count++; + return stream.str(); +} + @implementation FlutterHeadlessDartRunner { shell::ThreadHost _threadHost; std::unique_ptr _shell; } -- (void)runWithEntrypoint:(NSString*)entrypoint { +- (void)runWithEntrypointAndCallback:(NSString*)entrypoint + libraryUri:(NSString*)uri + completion:(FlutterHeadlessDartRunnerCallback)callback { if (_shell != nullptr || entrypoint.length == 0) { FXL_LOG(ERROR) << "This headless dart runner was already used to run some code."; return; } - const auto label = "io.flutter.headless"; + const auto label = CreateShellLabel(); // Create the threads to run the shell on. _threadHost = { @@ -59,6 +75,10 @@ - (void)runWithEntrypoint:(NSString*)entrypoint { auto settings = shell::SettingsFromCommandLine(shell::CommandLineFromNSProcessInfo()); + // These values set the name of the isolate for debugging. + settings.advisory_script_entrypoint = entrypoint.UTF8String; + settings.advisory_script_uri = uri.UTF8String; + // Create the shell. This is a blocking operation. _shell = shell::Shell::Create( std::move(task_runners), // task runners @@ -77,16 +97,61 @@ - (void)runWithEntrypoint:(NSString*)entrypoint { [[[FlutterDartProject alloc] initFromDefaultSourceForConfiguration] autorelease]; auto config = project.runConfiguration; - - config.SetEntrypoint(entrypoint.UTF8String); + config.SetEntrypointAndLibrary(entrypoint.UTF8String, uri.UTF8String); // Override the default run configuration with the specified entrypoint. _shell->GetTaskRunners().GetUITaskRunner()->PostTask( - fxl::MakeCopyable([engine = _shell->GetEngine(), config = std::move(config)]() mutable { + fxl::MakeCopyable([engine = _shell->GetEngine(), config = std::move(config), + callback = Block_copy(callback)]() mutable { + BOOL success = NO; + FXL_LOG(INFO) << "Attempting to launch background engine configuration..."; if (!engine || !engine->Run(std::move(config))) { FXL_LOG(ERROR) << "Could not launch engine with configuration."; + } else { + FXL_LOG(INFO) << "Background Isolate successfully started and run."; + success = YES; + } + if (callback != nil) { + callback(success); + Block_release(callback); } })); } +- (void)runWithEntrypoint:(NSString*)entrypoint { + [self runWithEntrypointAndCallback:entrypoint libraryUri:nil completion:nil]; +} + +#pragma mark - FlutterBinaryMessenger + +- (void)sendOnChannel:(NSString*)channel message:(NSData*)message { + [self sendOnChannel:channel message:message binaryReply:nil]; +} + +- (void)sendOnChannel:(NSString*)channel + message:(NSData*)message + binaryReply:(FlutterBinaryReply)callback { + NSAssert(channel, @"The channel must not be null"); + fxl::RefPtr response = + (callback == nil) ? nullptr + : fxl::MakeRefCounted( + ^(NSData* reply) { + callback(reply); + }, + _shell->GetTaskRunners().GetPlatformTaskRunner()); + fxl::RefPtr platformMessage = + (message == nil) ? fxl::MakeRefCounted(channel.UTF8String, response) + : fxl::MakeRefCounted( + channel.UTF8String, shell::GetVectorFromNSData(message), response); + + _shell->GetPlatformView()->DispatchPlatformMessage(platformMessage); +} + +- (void)setMessageHandlerOnChannel:(NSString*)channel + binaryMessageHandler:(FlutterBinaryMessageHandler)handler { + reinterpret_cast(_shell->GetPlatformView().get()) + ->GetPlatformMessageRouter() + .SetMessageHandler(channel.UTF8String, handler); +} + @end diff --git a/shell/platform/darwin/ios/headless_platform_view_ios.h b/shell/platform/darwin/ios/headless_platform_view_ios.h new file mode 100644 index 0000000000000..4b93080956cc0 --- /dev/null +++ b/shell/platform/darwin/ios/headless_platform_view_ios.h @@ -0,0 +1,38 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SHELL_PLATFORM_IOS_HEADLESS_PLATFORM_VIEW_IOS_H_ +#define SHELL_PLATFORM_IOS_HEADLESS_PLATFORM_VIEW_IOS_H_ + +#include + +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/common/platform_view.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.h" +#include "lib/fxl/functional/closure.h" +#include "lib/fxl/macros.h" + +namespace shell { + +class HeadlessPlatformViewIOS : public PlatformView { + public: + explicit HeadlessPlatformViewIOS(Delegate& delegate, + blink::TaskRunners task_runners); + virtual ~HeadlessPlatformViewIOS(); + + PlatformMessageRouter& GetPlatformMessageRouter(); + + private: + PlatformMessageRouter platform_message_router_; + + // |shell::PlatformView| + void HandlePlatformMessage(fxl::RefPtr message); + + FXL_DISALLOW_COPY_AND_ASSIGN(HeadlessPlatformViewIOS); +}; + +} // namespace shell + +#endif // SHELL_PLATFORM_IOS_HEADLESS_PLATFORM_VIEW_IOS_H_ \ No newline at end of file diff --git a/shell/platform/darwin/ios/headless_platform_view_ios.mm b/shell/platform/darwin/ios/headless_platform_view_ios.mm new file mode 100644 index 0000000000000..87ee75891a1e6 --- /dev/null +++ b/shell/platform/darwin/ios/headless_platform_view_ios.mm @@ -0,0 +1,20 @@ + +#include "flutter/shell/platform/darwin/ios/headless_platform_view_ios.h" + +namespace shell { + +HeadlessPlatformViewIOS::HeadlessPlatformViewIOS(PlatformView::Delegate& delegate, + blink::TaskRunners task_runners) + : PlatformView(delegate, std::move(task_runners)) {} + +HeadlessPlatformViewIOS::~HeadlessPlatformViewIOS() = default; + +PlatformMessageRouter& HeadlessPlatformViewIOS::GetPlatformMessageRouter() { + return platform_message_router_; +} + +// |shell::PlatformView| +void HeadlessPlatformViewIOS::HandlePlatformMessage(fxl::RefPtr message) { + platform_message_router_.HandlePlatformMessage(std::move(message)); +} +} \ No newline at end of file diff --git a/shell/platform/darwin/ios/platform_view_ios.h b/shell/platform/darwin/ios/platform_view_ios.h index 3dc29b8b78a2a..8d1de7a0c092a 100644 --- a/shell/platform/darwin/ios/platform_view_ios.h +++ b/shell/platform/darwin/ios/platform_view_ios.h @@ -14,14 +14,14 @@ #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" #include "flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/platform_message_router.h" +#include "flutter/shell/platform/darwin/ios/headless_platform_view_ios.h" #include "flutter/shell/platform/darwin/ios/ios_surface.h" #include "lib/fxl/functional/closure.h" #include "lib/fxl/macros.h" namespace shell { -class PlatformViewIOS final : public PlatformView { +class PlatformViewIOS final : public HeadlessPlatformViewIOS { public: explicit PlatformViewIOS(PlatformView::Delegate& delegate, blink::TaskRunners task_runners, @@ -30,8 +30,6 @@ class PlatformViewIOS final : public PlatformView { ~PlatformViewIOS() override; - PlatformMessageRouter& GetPlatformMessageRouter(); - FlutterViewController* GetOwnerViewController() const; void RegisterExternalTexture(int64_t id, NSObject* texture); @@ -59,9 +57,6 @@ class PlatformViewIOS final : public PlatformView { // |shell::PlatformView| void SetSemanticsEnabled(bool enabled) override; - // |shell::PlatformView| - void HandlePlatformMessage( - fxl::RefPtr message) override; // |shell::PlatformView| void UpdateSemantics(blink::SemanticsNodeUpdates update, diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index 8d77716252f2b..f1cfc70e35f33 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -21,7 +21,7 @@ blink::TaskRunners task_runners, FlutterViewController* owner_controller, FlutterView* owner_view) - : PlatformView(delegate, std::move(task_runners)), + : HeadlessPlatformViewIOS(delegate, std::move(task_runners)), owner_controller_(owner_controller), owner_view_(owner_view), ios_surface_(owner_view_.createSurface) { @@ -36,10 +36,6 @@ return owner_controller_; } -PlatformMessageRouter& PlatformViewIOS::GetPlatformMessageRouter() { - return platform_message_router_; -} - void PlatformViewIOS::RegisterExternalTexture(int64_t texture_id, NSObject* texture) { RegisterTexture(std::make_shared(texture_id, texture)); @@ -79,11 +75,6 @@ } } -// |shell::PlatformView| -void PlatformViewIOS::HandlePlatformMessage(fxl::RefPtr message) { - platform_message_router_.HandlePlatformMessage(std::move(message)); -} - // |shell::PlatformView| std::unique_ptr PlatformViewIOS::CreateVSyncWaiter() { return std::make_unique(task_runners_); diff --git a/testing/dart/plugin_utilities_test.dart b/testing/dart/plugin_utilities_test.dart new file mode 100644 index 0000000000000..89f43824bc2dc --- /dev/null +++ b/testing/dart/plugin_utilities_test.dart @@ -0,0 +1,43 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ui'; + +import 'package:test/test.dart'; + +String top() => "top"; + +class Foo { + const Foo(); + static int getInt() => 1; + double getDouble() => 1.0; +} + +const Foo foo = const Foo(); + +void main() { + test('PluginUtilities Callback Handles', () { + // Top level callback. + final hTop = PluginUtilities.getCallbackHandle(top); + expect(hTop, isNotNull); + expect(hTop, isNot(0)); + expect(PluginUtilities.getCallbackHandle(top), hTop); + final topClosure = PluginUtilities.getCallbackFromHandle(hTop); + expect(topClosure, isNotNull); + expect(topClosure(), "top"); + + // Static method callback + final hGetInt = PluginUtilities.getCallbackHandle(Foo.getInt); + expect(hGetInt, isNotNull); + expect(hGetInt, isNot(0)); + expect(PluginUtilities.getCallbackHandle(Foo.getInt), hGetInt); + final getIntClosure = PluginUtilities.getCallbackFromHandle(hGetInt); + expect(getIntClosure, isNotNull); + expect(getIntClosure(), 1); + + // Instance method callbacks cannot be looked up. + final foo = new Foo(); + expect(PluginUtilities.getCallbackHandle(foo.getDouble), isNull); + }); +} diff --git a/tools/licenses/pubspec.lock b/tools/licenses/pubspec.lock index 545aa1144973f..75d0f144fde64 100644 --- a/tools/licenses/pubspec.lock +++ b/tools/licenses/pubspec.lock @@ -1,61 +1,53 @@ # Generated by pub -# See https://www.dartlang.org/tools/pub/glossary#lockfile +# See http://pub.dartlang.org/doc/glossary.html#lockfile packages: archive: - dependency: "direct main" description: name: archive url: "https://pub.dartlang.org" source: hosted version: "1.0.33" args: - dependency: "direct main" description: name: args url: "https://pub.dartlang.org" source: hosted version: "0.13.7" charcode: - dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted version: "1.1.1" collection: - dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted version: "1.14.5" convert: - dependency: transitive description: name: convert url: "https://pub.dartlang.org" source: hosted version: "2.0.1" crypto: - dependency: "direct main" description: name: crypto url: "https://pub.dartlang.org" source: hosted version: "2.0.2+1" path: - dependency: "direct main" description: name: path url: "https://pub.dartlang.org" source: hosted version: "1.5.1" typed_data: - dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted version: "1.1.5" sdks: - dart: ">=1.21.0 <=2.0.0-edge.2b36f923d95a41b2f1c5cbb5edd9872f68c18112" + dart: ">=1.21.0 <2.0.0" diff --git a/travis/licenses_golden/licenses_flutter b/travis/licenses_golden/licenses_flutter index a14034994f4dd..29035a43eb7ae 100644 --- a/travis/licenses_golden/licenses_flutter +++ b/travis/licenses_golden/licenses_flutter @@ -206,6 +206,7 @@ FILE: ../../../flutter/shell/platform/darwin/desktop/Info.plist FILE: ../../../flutter/shell/platform/darwin/ios/framework/Flutter.podspec FILE: ../../../flutter/shell/platform/darwin/ios/framework/Info.plist FILE: ../../../flutter/shell/platform/darwin/ios/framework/module.modulemap +FILE: ../../../flutter/shell/platform/darwin/ios/headless_platform_view_ios.mm FILE: ../../../flutter/shell/platform/embedder/assets/EmbedderInfo.plist FILE: ../../../flutter/shell/platform/embedder/assets/embedder.modulemap FILE: ../../../flutter/shell/platform/embedder/fixtures/simple_main.dart @@ -489,6 +490,7 @@ FILE: ../../../flutter/flutter_kernel_transformers/lib/track_widget_constructor_ FILE: ../../../flutter/lib/ui/isolate_name_server.dart FILE: ../../../flutter/lib/ui/painting/image_encoding.cc FILE: ../../../flutter/lib/ui/painting/image_encoding.h +FILE: ../../../flutter/lib/ui/plugins.dart FILE: ../../../flutter/lib/ui/semantics/custom_accessibility_action.cc FILE: ../../../flutter/lib/ui/semantics/custom_accessibility_action.h FILE: ../../../flutter/lib/ui/text/asset_manager_font_provider.cc @@ -499,10 +501,13 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/Platfor FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistry.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewRegistryImpl.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm +FILE: ../../../flutter/shell/platform/darwin/ios/headless_platform_view_ios.h ---------------------------------------------------------------------------------------------------- Copyright 2018 The Chromium Authors. All rights reserved. @@ -590,12 +595,15 @@ FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server.cc FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server.h FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.cc FILE: ../../../flutter/lib/ui/isolate_name_server/isolate_name_server_natives.h +FILE: ../../../flutter/lib/ui/plugins/callback_cache.cc +FILE: ../../../flutter/lib/ui/plugins/callback_cache.h FILE: ../../../flutter/shell/common/isolate_configuration.cc FILE: ../../../flutter/shell/common/isolate_configuration.h FILE: ../../../flutter/shell/platform/android/android_shell_holder.cc FILE: ../../../flutter/shell/platform/android/android_shell_holder.h FILE: ../../../flutter/shell/platform/android/platform_message_response_android.cc FILE: ../../../flutter/shell/platform/android/platform_message_response_android.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate_Internal.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.mm ----------------------------------------------------------------------------------------------------