diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 7aeb58b5adf502271b36deaa1b945147d07eb3f0..ed0e4e1e7d7e00011575b21e687615165cedf90c 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -502,6 +502,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterRunArgument 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/FlutterCallbackCache_Internal.h 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 diff --git a/lib/ui/plugins/callback_cache.cc b/lib/ui/plugins/callback_cache.cc index 0e46493e247ca7268801d74a72b216d9b0725ebb..a80b956a03407a8d6ca396652081dfce21f526b9 100644 --- a/lib/ui/plugins/callback_cache.cc +++ b/lib/ui/plugins/callback_cache.cc @@ -2,17 +2,39 @@ // 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 +#include + +#include "flutter/fml/build_config.h" #include "flutter/fml/logging.h" +#include "flutter/fml/paths.h" +#include "flutter/lib/ui/plugins/callback_cache.h" +#include "third_party/rapidjson/rapidjson/document.h" +#include "third_party/rapidjson/rapidjson/stringbuffer.h" +#include "third_party/rapidjson/rapidjson/writer.h" #include "third_party/tonic/converter/dart_converter.h" +using rapidjson::Document; +using rapidjson::StringBuffer; +using rapidjson::Writer; using tonic::ToDart; namespace blink { +static const char* kHandleKey = "handle"; +static const char* kRepresentationKey = "representation"; +static const char* kNameKey = "name"; +static const char* kClassNameKey = "class_name"; +static const char* kLibraryPathKey = "library_path"; +static const char* kCacheName = "flutter_callback_cache.json"; std::mutex DartCallbackCache::mutex_; +std::string DartCallbackCache::cache_path_; std::map DartCallbackCache::cache_; +void DartCallbackCache::SetCachePath(const std::string& path) { + cache_path_ = fml::paths::JoinPaths({path, kCacheName}); +} + Dart_Handle DartCallbackCache::GetCallback(int64_t handle) { std::lock_guard lock(mutex_); auto iterator = cache_.find(handle); @@ -34,6 +56,7 @@ int64_t DartCallbackCache::GetCallbackHandle(const std::string& name, if (cache_.find(hash) == cache_.end()) { cache_[hash] = {name, class_name, library_path}; + SaveCacheToDisk(); } return hash; } @@ -48,6 +71,82 @@ DartCallbackCache::GetCallbackInformation(int64_t handle) { return nullptr; } +void DartCallbackCache::SaveCacheToDisk() { + // Cache JSON format + // [ + // { + // "hash": 42, + // "representation": { + // "name": "...", + // "class_name": "...", + // "library_path": "..." + // } + // }, + // { + // ... + // } + // ] + StringBuffer s; + Writer writer(s); + writer.StartArray(); + for (auto iterator = cache_.begin(); iterator != cache_.end(); ++iterator) { + int64_t hash = iterator->first; + DartCallbackRepresentation cb = iterator->second; + writer.StartObject(); + writer.Key(kHandleKey); + writer.Int64(hash); + writer.Key(kRepresentationKey); + writer.StartObject(); + writer.Key(kNameKey); + writer.String(cb.name.c_str()); + writer.Key(kClassNameKey); + writer.String(cb.class_name.c_str()); + writer.Key(kLibraryPathKey); + writer.String(cb.library_path.c_str()); + writer.EndObject(); + writer.EndObject(); + } + writer.EndArray(); + + std::ofstream output(cache_path_); + output << s.GetString(); + output.close(); +} + +void DartCallbackCache::LoadCacheFromDisk() { + std::lock_guard lock(mutex_); + + // Don't reload the cache if it's already populated. + if (!cache_.empty()) { + return; + } + std::ifstream input(cache_path_); + if (!input) { + return; + } + std::string cache_contents{std::istreambuf_iterator(input), + std::istreambuf_iterator()}; + Document d; + d.Parse(cache_contents.c_str()); + if (d.HasParseError() || !d.IsArray()) { + FML_LOG(WARNING) << "Could not parse callback cache, aborting restore"; + // TODO(bkonyi): log and bail (delete cache?) + return; + } + const auto entries = d.GetArray(); + for (auto it = entries.begin(); it != entries.end(); ++it) { + const auto root_obj = it->GetObject(); + const auto representation = root_obj[kRepresentationKey].GetObject(); + + const int64_t hash = root_obj[kHandleKey].GetInt64(); + DartCallbackRepresentation cb; + cb.name = representation[kNameKey].GetString(); + cb.class_name = representation[kClassNameKey].GetString(); + cb.library_path = representation[kLibraryPathKey].GetString(); + cache_[hash] = cb; + } +} + Dart_Handle DartCallbackCache::LookupDartClosure( const std::string& name, const std::string& class_name, diff --git a/lib/ui/plugins/callback_cache.h b/lib/ui/plugins/callback_cache.h index 04e919a47cb72025cdc1d5f19aca911cc465a5bf..b13002d332f9b8e893dd0e92157d82bec3e86a62 100644 --- a/lib/ui/plugins/callback_cache.h +++ b/lib/ui/plugins/callback_cache.h @@ -14,8 +14,6 @@ #include "flutter/fml/synchronization/thread_annotations.h" #include "third_party/dart/runtime/include/dart_api.h" -#define DART_CALLBACK_INVALID_HANDLE -1 - namespace blink { typedef struct { @@ -26,6 +24,9 @@ typedef struct { class DartCallbackCache { public: + static void SetCachePath(const std::string& path); + static std::string GetCachePath() { return cache_path_; } + static int64_t GetCallbackHandle(const std::string& name, const std::string& class_name, const std::string& library_path) @@ -36,12 +37,17 @@ class DartCallbackCache { static std::unique_ptr GetCallbackInformation( int64_t handle) FML_LOCKS_EXCLUDED(mutex_); + static void LoadCacheFromDisk() FML_LOCKS_EXCLUDED(mutex_); + private: static Dart_Handle LookupDartClosure(const std::string& name, const std::string& class_name, const std::string& library_path); + static void SaveCacheToDisk() FML_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + static std::mutex mutex_; + static std::string cache_path_; static std::map cache_ FML_GUARDED_BY(mutex_); diff --git a/shell/platform/android/flutter_main.cc b/shell/platform/android/flutter_main.cc index 2a9a46a97b3a9b40d2003be47ed51cd2f6825ef3..fbc0789faf7c192f621eb0903234b1eb987d3822 100644 --- a/shell/platform/android/flutter_main.cc +++ b/shell/platform/android/flutter_main.cc @@ -15,6 +15,7 @@ #include "flutter/fml/message_loop.h" #include "flutter/fml/paths.h" #include "flutter/fml/platform/android/jni_util.h" +#include "flutter/lib/ui/plugins/callback_cache.h" #include "flutter/runtime/dart_vm.h" #include "flutter/runtime/start_up.h" #include "flutter/shell/common/shell.h" @@ -44,7 +45,8 @@ void FlutterMain::Init(JNIEnv* env, jclass clazz, jobject context, jobjectArray jargs, - jstring bundlePath) { + jstring bundlePath, + jstring appStoragePath) { std::vector args; args.push_back("flutter"); for (auto& arg : fml::jni::StringArrayToVector(env, jargs)) { @@ -56,6 +58,11 @@ void FlutterMain::Init(JNIEnv* env, settings.assets_path = fml::jni::JavaStringToString(env, bundlePath); + // Restore the callback cache. + blink::DartCallbackCache::SetCachePath( + fml::jni::JavaStringToString(env, appStoragePath)); + blink::DartCallbackCache::LoadCacheFromDisk(); + if (!blink::DartVM::IsRunningPrecompiledCode()) { // Check to see if the appropriate kernel files are present and configure // settings accordingly. @@ -97,7 +104,7 @@ bool FlutterMain::Register(JNIEnv* env) { { .name = "nativeInit", .signature = "(Landroid/content/Context;[Ljava/lang/String;Ljava/" - "lang/String;)V", + "lang/String;Ljava/lang/String;)V", .fnPtr = reinterpret_cast(&Init), }, { diff --git a/shell/platform/android/flutter_main.h b/shell/platform/android/flutter_main.h index acd4a46366bd466a6825ff007895b8f13b43d1b8..6ad122f9990a2d70fbe8562e469001c771d722e6 100644 --- a/shell/platform/android/flutter_main.h +++ b/shell/platform/android/flutter_main.h @@ -31,7 +31,8 @@ class FlutterMain { jclass clazz, jobject context, jobjectArray jargs, - jstring bundlePath); + jstring bundlePath, + jstring appRootPath); FML_DISALLOW_COPY_AND_ASSIGN(FlutterMain); }; diff --git a/shell/platform/android/io/flutter/util/PathUtils.java b/shell/platform/android/io/flutter/util/PathUtils.java index 55d3fcc80b1b69e70637e5462b973d0f64328c8a..df187cbc0c1ec0c53ec79381f6ae60a1002726ef 100644 --- a/shell/platform/android/io/flutter/util/PathUtils.java +++ b/shell/platform/android/io/flutter/util/PathUtils.java @@ -7,6 +7,10 @@ package io.flutter.util; import android.content.Context; public final class PathUtils { + public static String getFilesDir(Context applicationContext) { + return applicationContext.getFilesDir().getPath(); + } + public static String getDataDirectory(Context applicationContext) { return applicationContext.getDir("flutter", Context.MODE_PRIVATE).getPath(); } diff --git a/shell/platform/android/io/flutter/view/FlutterMain.java b/shell/platform/android/io/flutter/view/FlutterMain.java index 66b2fff70783a22e2ad33505710cfff5bd1454c1..ccdfb6f4a1093f27497723fce7e581aaf48581b9 100644 --- a/shell/platform/android/io/flutter/view/FlutterMain.java +++ b/shell/platform/android/io/flutter/view/FlutterMain.java @@ -219,8 +219,9 @@ public class FlutterMain { } String appBundlePath = findAppBundlePath(applicationContext); + String appStoragePath = PathUtils.getFilesDir(applicationContext); nativeInit(applicationContext, shellArgs.toArray(new String[0]), - appBundlePath); + appBundlePath, appStoragePath); sInitialized = true; } catch (Exception e) { @@ -229,7 +230,7 @@ public class FlutterMain { } } - private static native void nativeInit(Context context, String[] args, String bundlePath); + private static native void nativeInit(Context context, String[] args, String bundlePath, String appStoragePath); private static native void nativeRecordStartTimestamp(long initTimeMillis); /** diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index ca640ed1345842d0b43dd03436e7ef10498f1753..3de4e258a7d7561a62ad103224aca391c7a3a5fc 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -46,6 +46,7 @@ shared_library("create_flutter_framework_dylib") { "framework/Source/FlutterAppDelegate.mm", "framework/Source/FlutterAppDelegate_Internal.h", "framework/Source/FlutterCallbackCache.mm", + "framework/Source/FlutterCallbackCache_Internal.h", "framework/Source/FlutterChannels.mm", "framework/Source/FlutterCodecs.mm", "framework/Source/FlutterDartProject.mm", diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h index 42fa50840cd2705a9ef0691643995a6facdfea37..799aa90fa0b30adbe98c797daeb6ab7e7edcf257 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h @@ -59,6 +59,15 @@ NS_ASSUME_NONNULL_BEGIN */ - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions; + +/** + Called if this plugin has been registered for `UIApplicationDelegate` callbacks. + + - Returns: `NO` if this plugin vetoes application launch. + */ +- (BOOL)application:(UIApplication*)application + willFinishLaunchingWithOptions:(NSDictionary*)launchOptions; + /** Called if this plugin has been registered for `UIApplicationDelegate` callbacks. */ @@ -293,8 +302,8 @@ NS_ASSUME_NONNULL_BEGIN @end /** - Implement this in the `UIAppDelegate` of your app to enable Flutter plugins to register themselves to the application - life cycle events. + Implement this in the `UIAppDelegate` of your app to enable Flutter plugins to register themselves + to the application life cycle events. */ @protocol FlutterAppLifeCycleProvider - (void)addApplicationLifeCycleDelegate:(NSObject*)delegate; diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h b/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h index bce71db7da1d5059d8b7cc7426d07c29cedef257..8c9c0358669ed83be4c1df93585fa43ad50d459a 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h @@ -15,7 +15,8 @@ NS_ASSUME_NONNULL_BEGIN FLUTTER_EXPORT @interface FlutterPluginAppLifeCycleDelegate : NSObject /** - Registers `delegate` to receive life cycle callbacks via this FlutterPluginAppLifecycleDelegate as long as it is alive. + Registers `delegate` to receive life cycle callbacks via this FlutterPluginAppLifecycleDelegate as + long as it is alive. `delegate` will only referenced weakly. */ @@ -29,6 +30,14 @@ FLUTTER_EXPORT - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions; +/** + Calls all plugins registered for `UIApplicationDelegate` callbacks. + + - Returns: `NO` if any plugin vetoes application launch. + */ +- (BOOL)application:(UIApplication*)application + willFinishLaunchingWithOptions:(NSDictionary*)launchOptions; + /** Calls all plugins registered for `UIApplicationDelegate` callbacks. */ @@ -74,8 +83,8 @@ FLUTTER_EXPORT fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles - the request. + Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until + some plugin handles the request. - Returns: `YES` if any plugin handles the request. */ @@ -84,16 +93,16 @@ FLUTTER_EXPORT options:(NSDictionary*)options; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles - the request. + Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until + some plugin handles the request. - Returns: `YES` if any plugin handles the request. */ - (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles - the request. + Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until + some plugin handles the request. - Returns: `YES` if any plugin handles the request. */ @@ -111,8 +120,8 @@ FLUTTER_EXPORT API_AVAILABLE(ios(9.0)); /** - Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles - the request. + Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until + some plugin handles the request. - Returns: `YES` if any plugin handles the request. */ @@ -121,8 +130,8 @@ FLUTTER_EXPORT completionHandler:(nonnull void (^)(void))completionHandler; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles - the request. + Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until + some plugin handles the request. - Returns: `YES` if any plugin handles the request. */ @@ -130,8 +139,8 @@ FLUTTER_EXPORT performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler; /** - Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles - the request. + Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until + some plugin handles the request. - Returns: `YES` if any plugin handles the request. */ - (BOOL)application:(UIApplication*)application diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm index a544d516c81578db1b453deff37633533b3c4a4a..763374b968c5b879bb25a6625b4306f7b39233fa 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm @@ -22,6 +22,11 @@ [super dealloc]; } +- (BOOL)application:(UIApplication*)application + willFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + return [_lifeCycleDelegate application:application willFinishLaunchingWithOptions:launchOptions]; +} + - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { return [_lifeCycleDelegate application:application didFinishLaunchingWithOptions:launchOptions]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache.mm b/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache.mm index 61ff0b165d3858cf30abd834c939769f2423bbd7..eb353e019bed4cc40429cb8c6a96ed756d9c2ab1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache.mm @@ -2,7 +2,7 @@ // 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" +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache_Internal.h" #include "flutter/lib/ui/plugins/callback_cache.h" @@ -23,4 +23,24 @@ return new_info; } -@end \ No newline at end of file ++ (void)setCachePath:(NSString*)path { + assert(path != nil); + blink::DartCallbackCache::SetCachePath([path UTF8String]); + NSString* cache_path = + [NSString stringWithUTF8String:blink::DartCallbackCache::GetCachePath().c_str()]; + // Set the "Do Not Backup" flag to ensure that the cache isn't moved off disk in + // low-memory situations. + if (![[NSFileManager defaultManager] fileExistsAtPath:cache_path]) { + [[NSFileManager defaultManager] createFileAtPath:cache_path contents:nil attributes:nil]; + NSError* error = nil; + NSURL* URL = [NSURL fileURLWithPath:cache_path]; + BOOL success = [URL setResourceValue:[NSNumber numberWithBool:YES] + forKey:NSURLIsExcludedFromBackupKey + error:&error]; + if (!success) { + NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error); + } + } +} + +@end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache_Internal.h new file mode 100644 index 0000000000000000000000000000000000000000..553c485dc2e9d8ae2a2a5fb7525c36cd1e7e9832 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache_Internal.h @@ -0,0 +1,16 @@ +// 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_INTERNAL_H_ +#define FLUTTER_FLUTTERCALLBACKCACHE_INTERNAL_H_ + +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h" + +@interface FlutterCallbackCache () + ++ (void)setCachePath:(NSString*)path; + +@end + +#endif // FLUTTER_FLUTTERCALLBACKCACHE_INTERNAL_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm b/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm index 81a05493b54a9e154801282b9aff3c40d5bcf2ee..e4d9c03cf9fe129c8c045c2d4eb0e133a1847c02 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm @@ -4,7 +4,12 @@ #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h" #include "flutter/fml/logging.h" +#include "flutter/fml/paths.h" +#include "flutter/lib/ui/plugins/callback_cache.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache_Internal.h" + +static const char* kCallbackCacheSubDir = "Library/Caches/"; @implementation FlutterPluginAppLifeCycleDelegate { UIBackgroundTaskIdentifier _debugBackgroundTask; @@ -15,6 +20,8 @@ - (instancetype)init { if (self = [super init]) { + std::string cachePath = fml::paths::JoinPaths({getenv("HOME"), kCallbackCacheSubDir}); + [FlutterCallbackCache setCachePath:[NSString stringWithUTF8String:cachePath.c_str()]]; _pluginDelegates = [[NSPointerArray weakObjectsPointerArray] retain]; } return self; @@ -51,6 +58,22 @@ static BOOL isPowerOfTwo(NSUInteger x) { return YES; } +- (BOOL)application:(UIApplication*)application + willFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + blink::DartCallbackCache::LoadCacheFromDisk(); + for (id plugin in [_pluginDelegates allObjects]) { + if (!plugin) { + continue; + } + if ([plugin respondsToSelector:_cmd]) { + if (![plugin application:application willFinishLaunchingWithOptions:launchOptions]) { + return NO; + } + } + } + return YES; +} + // Returns the key window's rootViewController, if it's a FlutterViewController. // Otherwise, returns nil. - (FlutterViewController*)rootFlutterViewController { diff --git a/sky/packages/sky_engine/LICENSE b/sky/packages/sky_engine/LICENSE index 823c1fa3dc1046a753c9a47a016b3647bb33a42c..149dbfb7f485b4ac4f0b2537e367262e46c468c3 100644 --- a/sky/packages/sky_engine/LICENSE +++ b/sky/packages/sky_engine/LICENSE @@ -11171,37 +11171,6 @@ distribution. contributors may be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------------- -topaz - -Copyright 2017, the Flutter project authors. Please see the AUTHORS file -for details. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR