diff options
author | lambroslambrou@chromium.org <lambroslambrou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-12 01:22:59 +0000 |
---|---|---|
committer | lambroslambrou@chromium.org <lambroslambrou@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-12 01:22:59 +0000 |
commit | 7dfc50a0ea9358a42d0026aceec333613876fdf5 (patch) | |
tree | 138eaafc0e31a52a634881f4ccee707740e05846 /remoting | |
parent | cdf47cdad1c4e60a3d6eb802e36f9c5216fbc524 (diff) | |
download | chromium_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.h | 33 | ||||
-rw-r--r-- | remoting/host/me2me_preference_pane.mm | 154 |
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 |