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); | |
Bill Hesse
2016/04/25 08:20:19
I'm surprised this is so small. But I grew up wit
ahe
2016/05/24 08:44:42
Acknowledged.
| |
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(); | |
Bill Hesse
2016/04/25 08:20:19
Could we pass in "print" to killAndResetSafari her
ahe
2016/05/24 08:44:42
Acknowledged.
| |
227 } | |
OLD | NEW |