OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 |
| 6 /// Helper program for killing and resetting all Safari settings to a known |
| 7 /// state that works well for testing dart2js output in Safari. |
| 8 /// |
| 9 /// Warning: this will delete all your Safari settings and bookmarks. |
| 10 library testing.reset_safari; |
| 11 |
| 12 import 'dart:async' show |
| 13 Future, |
| 14 Timer; |
| 15 |
| 16 import 'dart:io' show |
| 17 Directory, |
| 18 File, |
| 19 Platform, |
| 20 Process, |
| 21 ProcessResult; |
| 22 |
| 23 const String defaultSafariBundleLocation = "/Applications/Safari.app/"; |
| 24 |
| 25 const String relativeSafariLocation = "Contents/MacOS/Safari"; |
| 26 |
| 27 const String lsofLocation = "/usr/sbin/lsof"; |
| 28 |
| 29 const String killLocation = "/bin/kill"; |
| 30 |
| 31 const String pkillLocation = "/usr/bin/pkill"; |
| 32 |
| 33 const String safari = "com.apple.Safari"; |
| 34 |
| 35 const String defaultsLocation = "/usr/bin/defaults"; |
| 36 |
| 37 final List<String> safariSettings = <String>[ |
| 38 "Library/Caches/$safari", |
| 39 "Library/Safari", |
| 40 "Library/Saved Application State/$safari.savedState", |
| 41 "Library/Caches/Metadata/Safari", |
| 42 "Library/Preferences/$safari.plist", |
| 43 ]; |
| 44 |
| 45 const Duration defaultPollDelay = const Duration(milliseconds: 1); |
| 46 |
| 47 final String cpgi = "$safari.ContentPageGroupIdentifier"; |
| 48 |
| 49 final String knownSafariPreference = ''' |
| 50 { |
| 51 DefaultBrowserPromptingState2 = 2; |
| 52 StartPageViewControllerMode = 0; |
| 53 TestDriveOriginBrowser = 1; |
| 54 TestDriveUserDecision = 2; |
| 55 TestDriveState = 3; |
| 56 AlwaysRestoreSessionAtLaunch = 0; |
| 57 NewTabBehavior = 1; |
| 58 NewWindowBehavior = 1; |
| 59 LastSafariVersionWithWelcomePage = "9.0"; |
| 60 OpenNewTabsInFront = 0; |
| 61 TabCreationPolicy = 0; |
| 62 |
| 63 IncludeDevelopMenu = 1; |
| 64 WebKitDeveloperExtrasEnabledPreferenceKey = 1; |
| 65 "$cpgi.WebKit2DeveloperExtrasEnabled" = 1; |
| 66 |
| 67 AutoFillCreditCardData = 0; |
| 68 AutoFillMiscellaneousForms = 0; |
| 69 AutoFillPasswords = 0; |
| 70 |
| 71 SuppressSearchSuggestions = 1; |
| 72 |
| 73 PreloadTopHit = 0; |
| 74 ShowFavoritesUnderSmartSearchField = 0; |
| 75 WebsiteSpecificSearchEnabled = 0; |
| 76 |
| 77 WarnAboutFraudulentWebsites = 0; |
| 78 |
| 79 |
| 80 WebKitJavaScriptEnabled = 1; |
| 81 "$cpgi.WebKit2JavaScriptEnabled" = 1; |
| 82 |
| 83 WebKitJavaScriptCanOpenWindowsAutomatically = 1; |
| 84 "$cpgi.WebKit2JavaScriptCanOpenWindowsAutomatically" = 1; |
| 85 |
| 86 "$cpgi.WebKit2WebGLEnabled" = 1; |
| 87 WebGLDefaultLoadPolicy = WebGLPolicyAllowNoSecurityRestrictions; |
| 88 |
| 89 "$cpgi.WebKit2PluginsEnabled" = 0; |
| 90 |
| 91 BlockStoragePolicy = 1; |
| 92 WebKitStorageBlockingPolicy = 0; |
| 93 "$cpgi.WebKit2StorageBlockingPolicy" = 0; |
| 94 |
| 95 |
| 96 SafariGeolocationPermissionPolicy = 0; |
| 97 |
| 98 CanPromptForPushNotifications = 0; |
| 99 |
| 100 InstallExtensionUpdatesAutomatically = 0; |
| 101 |
| 102 ShowFullURLInSmartSearchField = 1; |
| 103 |
| 104 "$cpgi.WebKit2PlugInSnapshottingEnabled" = 0; |
| 105 } |
| 106 '''; |
| 107 |
| 108 Future<Null> get pollDelay => new Future.delayed(defaultPollDelay); |
| 109 |
| 110 String signalArgument( |
| 111 String defaultSignal, |
| 112 {bool force: false, |
| 113 bool testOnly: false}) { |
| 114 if (force && testOnly) { |
| 115 throw new ArgumentError("[force] and [testOnly] can't both be true."); |
| 116 } |
| 117 if (force) return "-KILL"; |
| 118 if (testOnly) return "-0"; |
| 119 return defaultSignal; |
| 120 } |
| 121 |
| 122 Future<int> kill( |
| 123 List<String> pids, |
| 124 {bool force: false, |
| 125 bool testOnly: false}) async { |
| 126 List<String> arguments = |
| 127 <String>[signalArgument("-TERM", force: force, testOnly: testOnly)] |
| 128 ..addAll(pids); |
| 129 ProcessResult result = await Process.run(killLocation, arguments); |
| 130 return result.exitCode; |
| 131 } |
| 132 |
| 133 Future<int> pkill( |
| 134 String pattern, |
| 135 {bool force: false, |
| 136 bool testOnly: false}) async { |
| 137 List<String> arguments = <String>[ |
| 138 signalArgument("-HUP", force: force, testOnly: testOnly), |
| 139 pattern]; |
| 140 ProcessResult result = await Process.run(pkillLocation, arguments); |
| 141 return result.exitCode; |
| 142 } |
| 143 |
| 144 Uri validatedBundleName(Uri bundle) { |
| 145 if (bundle == null) return Uri.base.resolve(defaultSafariBundleLocation); |
| 146 if (!bundle.path.endsWith("/")) { |
| 147 throw new ArgumentError( |
| 148 "Bundle ('$bundle') must end with a slash ('/')."); |
| 149 } |
| 150 return bundle; |
| 151 } |
| 152 |
| 153 Future<Null> killSafari({Uri bundle}) async { |
| 154 bundle = validatedBundleName(bundle); |
| 155 Uri safariBinary = bundle.resolve(relativeSafariLocation); |
| 156 ProcessResult result = await Process.run( |
| 157 lsofLocation, ["-t", safariBinary.toFilePath()]); |
| 158 if (result.exitCode == 0) { |
| 159 String stdout = result.stdout; |
| 160 List<String> pids = new List<String>.from( |
| 161 stdout.split("\n").where((String line) => !line.isEmpty)); |
| 162 Timer timer = new Timer(const Duration(seconds: 10), () { |
| 163 print("Kill -9 Safari $pids"); |
| 164 kill(pids, force: true); |
| 165 }); |
| 166 int exitCode = await kill(pids); |
| 167 while (exitCode == 0) { |
| 168 await pollDelay; |
| 169 print("Polling Safari $pids"); |
| 170 exitCode = await kill(pids, testOnly: true); |
| 171 } |
| 172 timer.cancel(); |
| 173 } |
| 174 Timer timer = new Timer(const Duration(seconds: 10), () { |
| 175 print("Kill -9 $safari"); |
| 176 pkill(safari, force: true); |
| 177 }); |
| 178 int exitCode = await pkill(safari); |
| 179 while (exitCode == 0) { |
| 180 await pollDelay; |
| 181 print("Polling $safari"); |
| 182 exitCode = await pkill(safari, testOnly: true); |
| 183 } |
| 184 timer.cancel(); |
| 185 } |
| 186 |
| 187 Future<Null> deleteIfExists(Uri uri) async { |
| 188 Directory directory = new Directory.fromUri(uri); |
| 189 if (await directory.exists()) { |
| 190 print("Deleting directory '$uri'."); |
| 191 await directory.delete(recursive: true); |
| 192 } else { |
| 193 File file = new File.fromUri(uri); |
| 194 if (await file.exists()) { |
| 195 print("Deleting file '$uri'."); |
| 196 await file.delete(); |
| 197 } else { |
| 198 print("File '$uri' not found."); |
| 199 } |
| 200 } |
| 201 } |
| 202 |
| 203 Future<Null> resetSafariSettings() async { |
| 204 String home = Platform.environment["HOME"]; |
| 205 if (!home.endsWith("/")) { |
| 206 home = "$home/"; |
| 207 } |
| 208 Uri homeDirectory = Uri.base.resolve(home); |
| 209 for (String setting in safariSettings) { |
| 210 await deleteIfExists(homeDirectory.resolve(setting)); |
| 211 } |
| 212 ProcessResult result = await Process.run( |
| 213 defaultsLocation, <String>["write", safari, knownSafariPreference]); |
| 214 if (result.exitCode != 0) { |
| 215 throw "Unable to reset Safari settings: ${result.stdout}${result.stderr}"; |
| 216 } |
| 217 } |
| 218 |
| 219 Future<Null> killAndResetSafari({Uri bundle}) async { |
| 220 bundle = validatedBundleName(bundle); |
| 221 await killSafari(bundle: bundle); |
| 222 await resetSafariSettings(); |
| 223 } |
| 224 |
| 225 Future<Null> main() async { |
| 226 await killAndResetSafari(); |
| 227 } |
OLD | NEW |