Index: tools/testing/dart/reset_safari.dart |
diff --git a/tools/testing/dart/reset_safari.dart b/tools/testing/dart/reset_safari.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e1886a5442ca861c7248ae4a9daffe21f0bbd88a |
--- /dev/null |
+++ b/tools/testing/dart/reset_safari.dart |
@@ -0,0 +1,227 @@ |
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+ |
+/// Helper program for killing and resetting all Safari settings to a known |
+/// state that works well for testing dart2js output in Safari. |
+/// |
+/// Warning: this will delete all your Safari settings and bookmarks. |
+library testing.reset_safari; |
+ |
+import 'dart:async' show |
+ Future, |
+ Timer; |
+ |
+import 'dart:io' show |
+ Directory, |
+ File, |
+ Platform, |
+ Process, |
+ ProcessResult; |
+ |
+const String defaultSafariBundleLocation = "/Applications/Safari.app/"; |
+ |
+const String relativeSafariLocation = "Contents/MacOS/Safari"; |
+ |
+const String lsofLocation = "/usr/sbin/lsof"; |
+ |
+const String killLocation = "/bin/kill"; |
+ |
+const String pkillLocation = "/usr/bin/pkill"; |
+ |
+const String safari = "com.apple.Safari"; |
+ |
+const String defaultsLocation = "/usr/bin/defaults"; |
+ |
+final List<String> safariSettings = <String>[ |
+ "Library/Caches/$safari", |
+ "Library/Safari", |
+ "Library/Saved Application State/$safari.savedState", |
+ "Library/Caches/Metadata/Safari", |
+ "Library/Preferences/$safari.plist", |
+]; |
+ |
+const Duration defaultPollDelay = const Duration(milliseconds: 1); |
+ |
+final String cpgi = "$safari.ContentPageGroupIdentifier"; |
+ |
+final String knownSafariPreference = ''' |
+{ |
+ DefaultBrowserPromptingState2 = 2; |
+ StartPageViewControllerMode = 0; |
+ TestDriveOriginBrowser = 1; |
+ TestDriveUserDecision = 2; |
+ TestDriveState = 3; |
+ AlwaysRestoreSessionAtLaunch = 0; |
+ NewTabBehavior = 1; |
+ NewWindowBehavior = 1; |
+ LastSafariVersionWithWelcomePage = "9.0"; |
+ OpenNewTabsInFront = 0; |
+ TabCreationPolicy = 0; |
+ |
+ IncludeDevelopMenu = 1; |
+ WebKitDeveloperExtrasEnabledPreferenceKey = 1; |
+ "$cpgi.WebKit2DeveloperExtrasEnabled" = 1; |
+ |
+ AutoFillCreditCardData = 0; |
+ AutoFillMiscellaneousForms = 0; |
+ AutoFillPasswords = 0; |
+ |
+ SuppressSearchSuggestions = 1; |
+ |
+ PreloadTopHit = 0; |
+ ShowFavoritesUnderSmartSearchField = 0; |
+ WebsiteSpecificSearchEnabled = 0; |
+ |
+ WarnAboutFraudulentWebsites = 0; |
+ |
+ |
+ WebKitJavaScriptEnabled = 1; |
+ "$cpgi.WebKit2JavaScriptEnabled" = 1; |
+ |
+ WebKitJavaScriptCanOpenWindowsAutomatically = 1; |
+ "$cpgi.WebKit2JavaScriptCanOpenWindowsAutomatically" = 1; |
+ |
+ "$cpgi.WebKit2WebGLEnabled" = 1; |
+ WebGLDefaultLoadPolicy = WebGLPolicyAllowNoSecurityRestrictions; |
+ |
+ "$cpgi.WebKit2PluginsEnabled" = 0; |
+ |
+ BlockStoragePolicy = 1; |
+ WebKitStorageBlockingPolicy = 0; |
+ "$cpgi.WebKit2StorageBlockingPolicy" = 0; |
+ |
+ |
+ SafariGeolocationPermissionPolicy = 0; |
+ |
+ CanPromptForPushNotifications = 0; |
+ |
+ InstallExtensionUpdatesAutomatically = 0; |
+ |
+ ShowFullURLInSmartSearchField = 1; |
+ |
+ "$cpgi.WebKit2PlugInSnapshottingEnabled" = 0; |
+} |
+'''; |
+ |
+Future<Null> get pollDelay => new Future.delayed(defaultPollDelay); |
+ |
+String signalArgument( |
+ String defaultSignal, |
+ {bool force: false, |
+ bool testOnly: false}) { |
+ if (force && testOnly) { |
+ throw new ArgumentError("[force] and [testOnly] can't both be true."); |
+ } |
+ if (force) return "-KILL"; |
+ if (testOnly) return "-0"; |
+ return defaultSignal; |
+} |
+ |
+Future<int> kill( |
+ List<String> pids, |
+ {bool force: false, |
+ bool testOnly: false}) async { |
+ List<String> arguments = |
+ <String>[signalArgument("-TERM", force: force, testOnly: testOnly)] |
+ ..addAll(pids); |
+ ProcessResult result = await Process.run(killLocation, arguments); |
+ return result.exitCode; |
+} |
+ |
+Future<int> pkill( |
+ String pattern, |
+ {bool force: false, |
+ bool testOnly: false}) async { |
+ List<String> arguments = <String>[ |
+ signalArgument("-HUP", force: force, testOnly: testOnly), |
+ pattern]; |
+ ProcessResult result = await Process.run(pkillLocation, arguments); |
+ return result.exitCode; |
+} |
+ |
+Uri validatedBundleName(Uri bundle) { |
+ if (bundle == null) return Uri.base.resolve(defaultSafariBundleLocation); |
+ if (!bundle.path.endsWith("/")) { |
+ throw new ArgumentError( |
+ "Bundle ('$bundle') must end with a slash ('/')."); |
+ } |
+ return bundle; |
+} |
+ |
+Future<Null> killSafari({Uri bundle}) async { |
+ bundle = validatedBundleName(bundle); |
+ Uri safariBinary = bundle.resolve(relativeSafariLocation); |
+ ProcessResult result = await Process.run( |
+ lsofLocation, ["-t", safariBinary.toFilePath()]); |
+ if (result.exitCode == 0) { |
+ String stdout = result.stdout; |
+ List<String> pids = new List<String>.from( |
+ stdout.split("\n").where((String line) => !line.isEmpty)); |
+ Timer timer = new Timer(const Duration(seconds: 10), () { |
+ print("Kill -9 Safari $pids"); |
+ kill(pids, force: true); |
+ }); |
+ int exitCode = await kill(pids); |
+ while (exitCode == 0) { |
+ await pollDelay; |
+ print("Polling Safari $pids"); |
+ exitCode = await kill(pids, testOnly: true); |
+ } |
+ timer.cancel(); |
+ } |
+ Timer timer = new Timer(const Duration(seconds: 10), () { |
+ print("Kill -9 $safari"); |
+ pkill(safari, force: true); |
+ }); |
+ int exitCode = await pkill(safari); |
+ while (exitCode == 0) { |
+ await pollDelay; |
+ print("Polling $safari"); |
+ exitCode = await pkill(safari, testOnly: true); |
+ } |
+ timer.cancel(); |
+} |
+ |
+Future<Null> deleteIfExists(Uri uri) async { |
+ Directory directory = new Directory.fromUri(uri); |
+ if (await directory.exists()) { |
+ print("Deleting directory '$uri'."); |
+ await directory.delete(recursive: true); |
+ } else { |
+ File file = new File.fromUri(uri); |
+ if (await file.exists()) { |
+ print("Deleting file '$uri'."); |
+ await file.delete(); |
+ } else { |
+ print("File '$uri' not found."); |
+ } |
+ } |
+} |
+ |
+Future<Null> resetSafariSettings() async { |
+ String home = Platform.environment["HOME"]; |
+ if (!home.endsWith("/")) { |
+ home = "$home/"; |
+ } |
+ Uri homeDirectory = Uri.base.resolve(home); |
+ for (String setting in safariSettings) { |
+ await deleteIfExists(homeDirectory.resolve(setting)); |
+ } |
+ ProcessResult result = await Process.run( |
+ defaultsLocation, <String>["write", safari, knownSafariPreference]); |
+ if (result.exitCode != 0) { |
+ throw "Unable to reset Safari settings: ${result.stdout}${result.stderr}"; |
+ } |
+} |
+ |
+Future<Null> killAndResetSafari({Uri bundle}) async { |
+ bundle = validatedBundleName(bundle); |
+ await killSafari(bundle: bundle); |
+ await resetSafariSettings(); |
+} |
+ |
+Future<Null> main() async { |
+ await killAndResetSafari(); |
+} |