summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
authorlambroslambrou@chromium.org <lambroslambrou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-12 01:22:59 +0000
committerlambroslambrou@chromium.org <lambroslambrou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-12 01:22:59 +0000
commit7dfc50a0ea9358a42d0026aceec333613876fdf5 (patch)
tree138eaafc0e31a52a634881f4ccee707740e05846 /remoting
parentcdf47cdad1c4e60a3d6eb802e36f9c5216fbc524 (diff)
downloadchromium_src-7dfc50a0ea9358a42d0026aceec333613876fdf5.zip
chromium_src-7dfc50a0ea9358a42d0026aceec333613876fdf5.tar.gz
chromium_src-7dfc50a0ea9358a42d0026aceec333613876fdf5.tar.bz2
Prompt user before restarting System Preferences.
When Chromoting Host components have been upgraded on Mac whilst the pref-pane is still loaded, prompt the user before restarting System Preferences. The user has the opportunity to cancel, in case they were doing something in a different pref-pane. In that case, the Chromoting pref-pane should be completely disabled, and shouldn't process any new config data until it is restarted. This is a followup CL to http://codereview.chromium.org/10413065/ BUG=129226 TEST=manual Review URL: https://chromiumcodereview.appspot.com/10446091 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@141583 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r--remoting/host/me2me_preference_pane.h33
-rw-r--r--remoting/host/me2me_preference_pane.mm154
2 files changed, 143 insertions, 44 deletions
diff --git a/remoting/host/me2me_preference_pane.h b/remoting/host/me2me_preference_pane.h
index 8ddf0b2..d3cfb06 100644
--- a/remoting/host/me2me_preference_pane.h
+++ b/remoting/host/me2me_preference_pane.h
@@ -65,10 +65,16 @@ class JsonHostConfig {
// True if launchd has been instructed to stop the service and we are waiting
// for the operation to complete.
BOOL awaiting_service_stop_;
+
+ // True if a version-mismatch has been detected. If true, this causes all
+ // controls to be greyed out, and also prevents any config file from being
+ // deleted, pending a restart of the preference pane.
+ BOOL restart_pending_or_canceled_;
}
- (void)mainViewDidLoad;
- (void)willSelect;
+- (void)didSelect;
- (void)willUnselect;
- (void)onDisable:(id)sender;
- (void)applyConfiguration:(id)sender
@@ -90,6 +96,9 @@ class JsonHostConfig {
// leaving a stale file around in case of a crash), but this method can safely
// be called multiple times without forgetting the loaded config. To explicitly
// forget the current config, set |have_new_config_| to NO.
+//
+// This method should not be called if |restart_pending_or_canceled_| is YES,
+// since this would delete any config file.
- (void)readNewConfig;
// Update all UI controls according to any stored flags and loaded config.
@@ -112,8 +121,26 @@ class JsonHostConfig {
- (BOOL)sendJobControlMessage:(const char*)launch_key;
// Compare the version of the running pref-pane against the installed version.
-// If the versions are mismatched, return YES and restart the System
-// Preferences application, so the correct version of this pref-pane is loaded.
-- (BOOL)restartPanelIfDifferentVersionInstalled;
+// If the versions are mismatched and the pref-pane is visible, disable the
+// pane to prevent interaction, and prompt the user to restart System
+// Preferences.
+//
+// This should be called on notification of a new config, and also in
+// |didSelect| when the pane becomes visible. The pane needs to be visible so
+// that the alert appears as a sheet over the pane (instead of a detached
+// window), which gives the user an appropriate context for the alert.
+//
+// In the case of a version-mismatch, the new config file should be kept until
+// System Preferences is restarted, or thrown away when the user cancels the
+// alert. This method sets the |restart_pending_or_canceled_| flag on
+// detecting version-mismatch.
+- (void)checkInstalledVersion;
+
+- (void)mismatchAlertDidEnd:(NSAlert*)alert
+ returnCode:(NSInteger)returnCode
+ contextInfo:(void*)contextInfo;
+
+// Called when the user chooses OK when prompted to restart System Preferences.
+- (void)restartSystemPreferences;
@end
diff --git a/remoting/host/me2me_preference_pane.mm b/remoting/host/me2me_preference_pane.mm
index 723a28b..35b9468 100644
--- a/remoting/host/me2me_preference_pane.mm
+++ b/remoting/host/me2me_preference_pane.mm
@@ -179,10 +179,18 @@ std::string JsonHostConfig::GetSerializedData() const {
repeats:YES] retain];
[self updateServiceStatus];
[self updateAuthorizationStatus];
- [self readNewConfig];
+
+ [self checkInstalledVersion];
+ if (!restart_pending_or_canceled_)
+ [self readNewConfig];
+
[self updateUI];
}
+- (void)didSelect {
+ [self checkInstalledVersion];
+}
+
- (void)willUnselect {
NSDistributedNotificationCenter* center =
[NSDistributedNotificationCenter defaultCenter];
@@ -191,9 +199,8 @@ std::string JsonHostConfig::GetSerializedData() const {
[service_status_timer_ invalidate];
[service_status_timer_ release];
service_status_timer_ = nil;
- if (have_new_config_) {
+ if (have_new_config_)
[self notifyPlugin:kUpdateFailedNotificationName];
- }
}
- (void)applyConfiguration:(id)sender
@@ -245,7 +252,10 @@ std::string JsonHostConfig::GetSerializedData() const {
}
- (void)onNewConfigFile:(NSNotification*)notification {
- [self readNewConfig];
+ [self checkInstalledVersion];
+ if (!restart_pending_or_canceled_)
+ [self readNewConfig];
+
[self updateUI];
}
@@ -281,12 +291,6 @@ std::string JsonHostConfig::GetSerializedData() const {
}
- (void)readNewConfig {
- if ([self restartPanelIfDifferentVersionInstalled]) {
- // Don't read any new config if the version is mismatched. Return control
- // to the main run-loop instead, so the application can be terminated.
- return;
- }
-
std::string file;
if (!GetTemporaryConfigFilePath(&file)) {
LOG(ERROR) << "Failed to get path of configuration data.";
@@ -351,11 +355,16 @@ std::string JsonHostConfig::GetSerializedData() const {
DCHECK(result);
}
- [disable_view_ setEnabled:(is_pane_unlocked_ && is_service_running_)];
- [confirm_pin_view_ setEnabled:is_pane_unlocked_];
+ [disable_view_ setEnabled:(is_pane_unlocked_ && is_service_running_ &&
+ !restart_pending_or_canceled_)];
+ [confirm_pin_view_ setEnabled:(is_pane_unlocked_ &&
+ !restart_pending_or_canceled_)];
[confirm_pin_view_ setEmail:base::SysUTF8ToNSString(email)];
NSString* applyButtonText = is_service_running_ ? @"Confirm" : @"Enable";
[confirm_pin_view_ setButtonText:applyButtonText];
+
+ if (restart_pending_or_canceled_)
+ [authorization_view_ setEnabled:NO];
}
- (void)showError {
@@ -529,41 +538,104 @@ std::string JsonHostConfig::GetSerializedData() const {
userInfo:nil];
}
-- (BOOL)restartPanelIfDifferentVersionInstalled {
- NSBundle* this_bundle = [NSBundle bundleForClass:[self class]];
- NSDictionary* this_plist = [this_bundle infoDictionary];
- NSString* this_version = [this_plist objectForKey:@"CFBundleVersion"];
+- (void)checkInstalledVersion {
+ // There's no point repeating the check if the pane has already been disabled
+ // from a previous call to this method. The pane only gets disabled when a
+ // version-mismatch has been detected here, so skip the check, but continue to
+ // handle the version-mismatch case.
+ if (!restart_pending_or_canceled_) {
+ NSBundle* this_bundle = [NSBundle bundleForClass:[self class]];
+ NSDictionary* this_plist = [this_bundle infoDictionary];
+ NSString* this_version = [this_plist objectForKey:@"CFBundleVersion"];
+
+ NSString* bundle_path = [this_bundle bundlePath];
+ NSString* plist_path =
+ [bundle_path stringByAppendingString:@"/Contents/Info.plist"];
+ NSDictionary* disk_plist =
+ [NSDictionary dictionaryWithContentsOfFile:plist_path];
+ NSString* disk_version = [disk_plist objectForKey:@"CFBundleVersion"];
+
+ if (disk_version == nil) {
+ LOG(ERROR) << "Failed to get installed version information";
+ [self showError];
+ return;
+ }
- NSString* bundle_path = [this_bundle bundlePath];
- NSString* plist_path =
- [bundle_path stringByAppendingString:@"/Contents/Info.plist"];
- NSDictionary* disk_plist =
- [NSDictionary dictionaryWithContentsOfFile:plist_path];
- NSString* disk_version = [disk_plist objectForKey:@"CFBundleVersion"];
+ if ([this_version isEqualToString:disk_version])
+ return;
- if (disk_version == nil) {
- LOG(ERROR) << "Failed to get installed version information";
- [self showError];
- return NO;
+ restart_pending_or_canceled_ = YES;
+ [self updateUI];
}
- if ([this_version isEqualToString:disk_version]) {
- return NO;
+ NSWindow* window = [[self mainView] window];
+ if (window == nil) {
+ // Defer the alert until |didSelect| is called, which happens just after
+ // the window is created.
+ return;
+ }
+
+ // This alert appears as a sheet over the top of the Chromoting pref-pane,
+ // underneath the title, so it's OK to refer to "this preference pane" rather
+ // than repeat the title "Chromoting" here.
+ NSAlert* alert = [[NSAlert alloc] init];
+ [alert setMessageText:@"System update detected"];
+ [alert setInformativeText:@"To use this preference pane, System Preferences "
+ "needs to be restarted"];
+ [alert addButtonWithTitle:@"OK"];
+ NSButton* cancel_button = [alert addButtonWithTitle:@"Cancel"];
+ [cancel_button setKeyEquivalent:@"\e"];
+ [alert setAlertStyle:NSWarningAlertStyle];
+ [alert beginSheetModalForWindow:window
+ modalDelegate:self
+ didEndSelector:@selector(
+ mismatchAlertDidEnd:returnCode:contextInfo:)
+ contextInfo:nil];
+ [alert release];
+}
+
+- (void)mismatchAlertDidEnd:(NSAlert*)alert
+ returnCode:(NSInteger)returnCode
+ contextInfo:(void*)contextInfo {
+ if (returnCode == NSAlertFirstButtonReturn) {
+ // OK was pressed.
+
+ // Dismiss the alert window here, so that the application will respond to
+ // the NSApp terminate: message.
+ [[alert window] orderOut:nil];
+ [self restartSystemPreferences];
} else {
- // Terminate and relaunch System Preferences.
-
- // TODO(lambroslambrou): Improve this when System Preferences supports
- // dynamic reloading of pref-panes.
- NSTask* task = [[NSTask alloc] init];
- NSArray* arguments = [NSArray arrayWithObjects:@"--relaunch-prefpane", nil];
- [task setLaunchPath:[NSString stringWithUTF8String:kHelperTool]];
- [task setArguments:arguments];
- [task setStandardInput:[NSPipe pipe]];
- [task launch];
- [task release];
- [NSApp terminate:nil];
- return YES;
+ // Cancel was pressed.
+
+ // If there is a new config file, delete it and notify the web-app of
+ // failure to apply the config. Otherwise, the web-app will remain in a
+ // spinning state until System Preferences eventually gets restarted and
+ // the user visits this pane again.
+ std::string file;
+ if (!GetTemporaryConfigFilePath(&file)) {
+ // There's no point in alerting the user here. The same error would
+ // happen when the pane is eventually restarted, so the user would be
+ // alerted at that time.
+ LOG(ERROR) << "Failed to get path of configuration data.";
+ return;
+ }
+ if (access(file.c_str(), F_OK) != 0)
+ return;
+
+ remove(file.c_str());
+ [self notifyPlugin:kUpdateFailedNotificationName];
}
}
+- (void)restartSystemPreferences {
+ NSTask* task = [[NSTask alloc] init];
+ NSArray* arguments = [NSArray arrayWithObjects:@"--relaunch-prefpane", nil];
+ [task setLaunchPath:[NSString stringWithUTF8String:kHelperTool]];
+ [task setArguments:arguments];
+ [task setStandardInput:[NSPipe pipe]];
+ [task launch];
+ [task release];
+ [NSApp terminate:nil];
+}
+
@end