Bug 561644: Link Handlers preference page blocks UI

First show the table of handlers disabled and without the handler
locations. Instead show "Loading..." in the handler location column.
Read the handler registration in the OS via a Job. Once this job is
finished refresh the table and enable it.

Change-Id: I7a0dbfc6a1d96c844897bbbca1c4549bad1ede7e
Also-by: Matthias Becker <ma.becker@sap.com>
Signed-off-by: Marcus Hoepfner <marcus.hoepfner@sap.com>
diff --git a/bundles/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/dialogs/UriSchemeHandlerPreferencePage.java b/bundles/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/dialogs/UriSchemeHandlerPreferencePage.java
index 98fde8f..b29fcb0 100644
--- a/bundles/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/dialogs/UriSchemeHandlerPreferencePage.java
+++ b/bundles/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/dialogs/UriSchemeHandlerPreferencePage.java
@@ -25,6 +25,7 @@
 import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_Handler_Label;
 import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_Handler_Text_No_Application;
 import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_LauncherCannotBeDetermined;
+import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_LoadingText;
 import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_Page_Description;
 import static org.eclipse.ui.internal.ide.IDEWorkbenchMessages.UrlHandlerPreferencePage_UnsupportedOperatingSystem;
 
@@ -33,9 +34,11 @@
 import java.util.Collections;
 import java.util.List;
 
+import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
 import org.eclipse.jface.dialogs.IDialogConstants;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.layout.GridDataFactory;
@@ -61,6 +64,7 @@
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.swt.widgets.Table;
 import org.eclipse.swt.widgets.TableColumn;
@@ -91,9 +95,12 @@
 	IMessageDialogWrapper messageDialogWrapper = new IMessageDialogWrapper() {
 	};
 
+	OsRegistrationReadingJob osRegistrationReadingJob = new OsRegistrationReadingJob();
+
 	IOperatingSystemRegistration operatingSystemRegistration = null;
 	IUriSchemeExtensionReader extensionReader = null;
 	private Composite handlerComposite;
+	private volatile boolean isLoading = false;
 
 	@SuppressWarnings("javadoc")
 	public UriSchemeHandlerPreferencePage() {
@@ -124,17 +131,31 @@
 		addFiller(parent);
 		createTableViewerForSchemes(parent);
 		createHandlerLocationControls(parent);
+
 		if (operatingSystemRegistration == null) {
 			setErrorMessage(NLS.bind(UrlHandlerPreferencePage_UnsupportedOperatingSystem,
 					Platform.isRunning() ? Platform.getOS() : null)); // running check for plain JUnit tests
-			tableViewer.getControl().setEnabled(false);
+			setDataOnTableViewer(Collections.emptyList());
+
 		} else if (currentLocation == null) {
 			setErrorMessage(UrlHandlerPreferencePage_LauncherCannotBeDetermined);
-			tableViewer.getControl().setEnabled(false);
+			setDataOnTableViewer(Collections.emptyList());
+
+		} else {
+			setDataOnTableViewer(getLoadingSchemeInformationList());
+			startRegistrationReadingJob();
 		}
+		tableViewer.getControl().setEnabled(false);
+
 		return parent;
 	}
 
+	private void startRegistrationReadingJob() {
+		isLoading = true;
+		osRegistrationReadingJob.setSystem(true);
+		osRegistrationReadingJob.schedule();
+	}
+
 	private void addFiller(Composite composite) {
 		PixelConverter pixelConverter = new PixelConverter(composite);
 		GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
@@ -175,16 +196,10 @@
 		TableSchemeSelectionListener listener = new TableSchemeSelectionListener();
 		tableViewer.addSelectionChangedListener(listener);
 		tableViewer.addCheckStateListener(listener);
+	}
 
-		Collection<UiSchemeInformation> schemeInformationList = Collections.emptyList();
+	private void setDataOnTableViewer(Collection<UiSchemeInformation> schemeInformationList) {
 		// Gets the schemes from extension points for URI schemes
-		try {
-			schemeInformationList = retrieveSchemeInformationList();
-		} catch (Exception e) {
-			IStatus status = new Status(IStatus.ERROR, IDEWorkbenchPlugin.IDE_WORKBENCH, 1,
-					UrlHandlerPreferencePage_Error_Reading_Scheme, e);
-			statusManagerWrapper.handle(status, StatusManager.BLOCK | StatusManager.LOG);
-		}
 		tableViewer.setInput(schemeInformationList);
 
 		for (UiSchemeInformation schemeInformation : schemeInformationList) {
@@ -212,13 +227,26 @@
 	 * @return the supported and registered URI schemes of this instance of eclipse
 	 * @throws Exception
 	 */
-	private Collection<UiSchemeInformation> retrieveSchemeInformationList() throws Exception {
+	private Collection<UiSchemeInformation> retrieveSchemeInformationList() {
 		Collection<UiSchemeInformation> returnList = new ArrayList<>();
 		Collection<IScheme> schemes = extensionReader.getSchemes();
-		if (operatingSystemRegistration != null) {
+		try {
 			for (ISchemeInformation info : operatingSystemRegistration.getSchemesInformation(schemes)) {
 				returnList.add(new UiSchemeInformation(info.isHandled(), info));
 			}
+		} catch (Exception e) {
+			IStatus status = new Status(IStatus.ERROR, IDEWorkbenchPlugin.IDE_WORKBENCH, 1,
+					UrlHandlerPreferencePage_Error_Reading_Scheme, e);
+			statusManagerWrapper.handle(status, StatusManager.BLOCK | StatusManager.LOG);
+		}
+		return returnList;
+	}
+
+	private Collection<UiSchemeInformation> getLoadingSchemeInformationList() {
+		Collection<UiSchemeInformation> returnList = new ArrayList<>();
+		Collection<IScheme> schemes = extensionReader.getSchemes();
+		for (IScheme scheme : schemes) {
+			returnList.add(new LoadingSchemeInformation(scheme));
 		}
 		return returnList;
 	}
@@ -226,7 +254,7 @@
 	@SuppressWarnings("unchecked")
 	@Override
 	public boolean performOk() {
-		if (operatingSystemRegistration == null || currentLocation == null) {
+		if (operatingSystemRegistration == null || currentLocation == null || isLoading) {
 			return true;
 		}
 
@@ -295,8 +323,8 @@
 					boolean answer = messageDialogWrapper.openQuestion(getShell(),
 							UriHandlerPreferencePage_Warning_OtherApp_Confirmation,
 							NLS.bind(UriHandlerPreferencePage_Warning_OtherApp_Confirmation_Description,
-							schemeInformation.information.getHandlerInstanceLocation(),
-							schemeInformation.information.getName()));
+									schemeInformation.information.getHandlerInstanceLocation(),
+									schemeInformation.information.getName()));
 					if (answer == false) {
 						schemeInformation.checked = false;
 						tableViewer.setChecked(schemeInformation, schemeInformation.checked);
@@ -341,14 +369,16 @@
 				UiSchemeInformation schemeInfo = (UiSchemeInformation) element;
 				switch (columnIndex) {
 				case 0:
-					return schemeInfo.information.getName();
+					return schemeInfo.getName();
+
 				case 1:
-					return schemeInfo.information.getDescription();
+					return schemeInfo.getDescription();
 				case 2:
 					String text = ""; //$NON-NLS-1$
-					if (schemeInfo.checked) {
+					if (UrlHandlerPreferencePage_LoadingText.equals(schemeInfo.getHandlerInstanceLocation()))
+						text = schemeInfo.getHandlerInstanceLocation();
+					else if (schemeInfo.isChecked()) {
 						text = UrlHandlerPreferencePage_Column_Handler_Text_Current_Application;
-
 					} else if (schemeIsHandledByOther(schemeInfo.information)) {
 						text = UrlHandlerPreferencePage_Column_Handler_Text_Other_Application;
 					}
@@ -366,7 +396,7 @@
 		}
 	}
 
-	final static class UiSchemeInformation {
+	static class UiSchemeInformation {
 		public boolean checked;
 		public ISchemeInformation information;
 
@@ -374,6 +404,53 @@
 			this.checked = checked;
 			this.information = information;
 		}
+
+		public String getHandlerInstanceLocation() {
+			return information.getHandlerInstanceLocation();
+		}
+
+		public String getName() {
+			return information.getName();
+		}
+
+		public String getDescription() {
+			return information.getDescription();
+		}
+
+		public boolean isChecked() {
+			return checked;
+		}
+	}
+
+	final static class LoadingSchemeInformation extends UiSchemeInformation {
+
+		private IScheme scheme;
+
+		public LoadingSchemeInformation(IScheme scheme) {
+			super(false, null);
+			this.scheme = scheme;
+		}
+
+		@Override
+		public String getName() {
+			return scheme.getName();
+		}
+
+		@Override
+		public String getDescription() {
+			return scheme.getDescription();
+		}
+
+		@Override
+		public String getHandlerInstanceLocation() {
+			return UrlHandlerPreferencePage_LoadingText;
+		}
+
+		@Override
+		public boolean isChecked() {
+			return false;
+		}
+
 	}
 
 	interface IStatusManagerWrapper {
@@ -388,8 +465,8 @@
 		}
 
 		default boolean openQuestion(Shell parent, String title, String message) {
-			MessageDialog dlg = new MessageDialog(parent, title, null, message, MessageDialog.CONFIRM,
-					0, UriHandlerPreferencePage_Confirm_Handle, IDialogConstants.CANCEL_LABEL);
+			MessageDialog dlg = new MessageDialog(parent, title, null, message, MessageDialog.CONFIRM, 0,
+					UriHandlerPreferencePage_Confirm_Handle, IDialogConstants.CANCEL_LABEL);
 			dlg.open();
 			if (dlg.getReturnCode() != IDialogConstants.OK_ID) {
 				return false;
@@ -397,4 +474,29 @@
 			return true;
 		}
 	}
+
+	class OsRegistrationReadingJob extends Job {
+		private OsRegistrationReadingJob() {
+			super("Retrieving Link Handlers registration status from Operating System"); //$NON-NLS-1$
+		}
+
+		@Override
+		protected IStatus run(IProgressMonitor monitor) {
+			try {
+				Collection<UiSchemeInformation> schemeInformationList = retrieveSchemeInformationList();
+				if (!schemeInformationList.isEmpty()) {
+					Display.getDefault().asyncExec(() -> {
+						if (!UriSchemeHandlerPreferencePage.this.tableViewer.getControl().isDisposed()) {
+							setDataOnTableViewer(schemeInformationList);
+							UriSchemeHandlerPreferencePage.this.tableViewer.getControl().setEnabled(true);
+						}
+					});
+				}
+			} finally {
+				UriSchemeHandlerPreferencePage.this.isLoading = false;
+			}
+			return Status.OK_STATUS;
+		}
+	}
+
 }
diff --git a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java
index 4b82a51..f754499 100644
--- a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java
+++ b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java
@@ -534,6 +534,7 @@
 	public static String UrlHandlerPreferencePage_Handler_Label;
 	public static String UrlHandlerPreferencePage_Handler_Text_No_Application;
 	public static String UrlHandlerPreferencePage_Page_Description;
+	public static String UrlHandlerPreferencePage_LoadingText;
 	public static String UrlHandlerPreferencePage_ColumnName_SchemeName;
 	public static String UrlHandlerPreferencePage_ColumnName_SchemeDescription;
 	public static String UrlHandlerPreferencePage_ColumnName_Handler;
diff --git a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties
index 628917c..c080296 100644
--- a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties
+++ b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties
@@ -512,6 +512,7 @@
 UrlHandlerPreferencePage_Handler_Label=Application:
 UrlHandlerPreferencePage_Handler_Text_No_Application=No application
 UrlHandlerPreferencePage_Page_Description=Link handlers define how your application deals with hyperlinks using a given scheme.\nThis page allows you to enable and disable link handlers in this application. 
+UrlHandlerPreferencePage_LoadingText=Loading...
 UrlHandlerPreferencePage_ColumnName_SchemeName=Scheme 
 UrlHandlerPreferencePage_ColumnName_SchemeDescription=Description 
 UrlHandlerPreferencePage_ColumnName_Handler=Handler 
diff --git a/tests/org.eclipse.ui.ide.application.tests/src/org/eclipse/ui/internal/ide/application/dialogs/UriSchemeHandlerPreferencePageTest.java b/tests/org.eclipse.ui.ide.application.tests/src/org/eclipse/ui/internal/ide/application/dialogs/UriSchemeHandlerPreferencePageTest.java
index b99dbbd..56d9bf7 100644
--- a/tests/org.eclipse.ui.ide.application.tests/src/org/eclipse/ui/internal/ide/application/dialogs/UriSchemeHandlerPreferencePageTest.java
+++ b/tests/org.eclipse.ui.ide.application.tests/src/org/eclipse/ui/internal/ide/application/dialogs/UriSchemeHandlerPreferencePageTest.java
@@ -26,6 +26,7 @@
 import org.eclipse.osgi.util.NLS;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.swt.widgets.TableItem;
@@ -80,7 +81,7 @@
 	@Test
 	public void schemesShown() throws Exception {
 		this.page.createContents(this.page.getShell());
-
+		waitForJob();
 		assertScheme(getTableItem(0), false, noAppSchemeInfo);
 		assertScheme(getTableItem(1), true, thisAppSchemeInfo);
 		assertScheme(getTableItem(2), false, otherAppSchemeInfo);
@@ -89,15 +90,16 @@
 	@Test
 	public void handlerTextShown() throws Exception {
 		this.page.createContents(this.page.getShell());
-
+		waitForJob();
 		assertHandlerTextForSelection(page, 0, NO_APPLICATION);
 		assertHandlerTextForSelection(page, 1, THIS_ECLIPSE_HANDLER_LOCATION);
 		assertHandlerTextForSelection(page, 2, OTHER_ECLIPSE_HANDLER_LOCATION);
 	}
 
 	@Test
-	public void checkNoAppScheme() {
+	public void checkNoAppScheme() throws Exception {
 		this.page.createContents(this.page.getShell());
+		waitForJob();
 
 		clickTableViewerCheckbox(0, true);
 
@@ -105,8 +107,9 @@
 	}
 
 	@Test
-	public void uncheckThisAppScheme() {
+	public void uncheckThisAppScheme() throws Exception {
 		this.page.createContents(this.page.getShell());
+		waitForJob();
 
 		clickTableViewerCheckbox(1, false);
 
@@ -114,8 +117,9 @@
 	}
 
 	@Test
-	public void checkOtherAppSchemeGivesWarningAndRevertsClick() {
+	public void checkOtherAppSchemeGivesWarningAndRevertsClick() throws Exception {
 		this.page.createContents(this.page.getShell());
+		waitForJob();
 
 		clickTableViewerCheckbox(2, true);
 
@@ -132,8 +136,10 @@
 	}
 
 	@Test
-	public void checkOtherAppSchemeOnWindowsIsAllowed() {
+	public void checkOtherAppSchemeOnWindowsIsAllowed() throws Exception {
 		this.page.createContents(this.page.getShell());
+		waitForJob();
+
 		operatingSystemRegistration.canOverwriteOtherApplicationsRegistration = true;
 		messageDialogSpy.actualAnswer = true;
 
@@ -152,8 +158,10 @@
 	}
 
 	@Test
-	public void checkOtherAppSchemeOnWindowsIsAllowedButNothingChangesWhenUserSaysNo() {
+	public void checkOtherAppSchemeOnWindowsIsAllowedButNothingChangesWhenUserSaysNo() throws Exception {
 		this.page.createContents(this.page.getShell());
+		waitForJob();
+
 		operatingSystemRegistration.canOverwriteOtherApplicationsRegistration = true;
 		messageDialogSpy.actualAnswer = false;
 
@@ -172,9 +180,9 @@
 	}
 
 	@Test
-	public void registersSchemesInOperatingSystemOnApply() {
+	public void registersSchemesInOperatingSystemOnApply() throws Exception {
 		this.page.createContents(this.page.getShell());
-
+		waitForJob();
 		clickTableViewerCheckbox(0, true);
 		clickTableViewerCheckbox(1, false);
 		page.performOk();
@@ -188,8 +196,9 @@
 	}
 
 	@Test
-	public void doesNotRegistersSchemesInOperatingSystemOnCancel() {
+	public void doesNotRegistersSchemesInOperatingSystemOnCancel() throws Exception {
 		this.page.createContents(this.page.getShell());
+		waitForJob();
 
 		clickTableViewerCheckbox(0, true);
 		clickTableViewerCheckbox(1, false);
@@ -202,21 +211,38 @@
 	}
 
 	@Test
-	public void showsErrorOnOperatingSystemRegistrationReadError() {
+	public void doesNotRegistersSchemesInOperatingSystemOnApplyWhenLoading() throws Exception {
+		this.page.createContents(this.page.getShell());
+
+		// tableItem[1] (hello1 scheme) is true (refer to members), but page is still
+		// loading
+		page.performOk();
+
+		OperatingSystemRegistrationMock mock = (OperatingSystemRegistrationMock) page.operatingSystemRegistration;
+		assertEquals(0, mock.addedSchemes.size());
+
+		assertEquals(0, mock.removedSchemes.size());
+		waitForJob();
+	}
+
+	@Test
+	public void showsErrorOnOperatingSystemRegistrationReadError() throws Exception {
 		OperatingSystemRegistrationMock mock = (OperatingSystemRegistrationMock) page.operatingSystemRegistration;
 		mock.schemeInformationReadException = new IOExceptionWithoutStackTrace("Error reading from OS");
 
 		this.page.createContents(this.page.getShell());
+		waitForJob();
 
 		assertErrorStatusRaised(IDEWorkbenchMessages.UrlHandlerPreferencePage_Error_Reading_Scheme);
 	}
 
 	@Test
-	public void showsErrorOnOperatingSystemRegistrationWriteError() {
+	public void showsErrorOnOperatingSystemRegistrationWriteError() throws Exception {
 		OperatingSystemRegistrationMock mock = (OperatingSystemRegistrationMock) page.operatingSystemRegistration;
 		mock.schemeInformationRegisterException = new IOExceptionWithoutStackTrace("Error writing into OS");
 
 		this.page.createContents(this.page.getShell());
+		waitForJob();
 
 		page.performOk();
 
@@ -251,6 +277,23 @@
 		assertTrue(page.performOk());
 	}
 
+	@Test
+	public void loadingSchemesShownAfterPageOpened() throws Exception {
+		this.page.createContents(this.page.getShell());
+		assertLoadingScheme(getTableItem(0), noAppSchemeInfo);
+		assertLoadingScheme(getTableItem(1), thisAppSchemeInfo);
+		assertLoadingScheme(getTableItem(2), otherAppSchemeInfo);
+		waitForJob();
+	}
+
+	private void waitForJob() throws InterruptedException {
+		this.page.osRegistrationReadingJob.join();
+		// jobs sets data asynchronously in TableViewer
+		Display display = Display.getCurrent();
+		while (display.readAndDispatch()) {
+		}
+	}
+
 	private void clickTableViewerCheckbox(int itemIndex, boolean checked) {
 		TableItem item = page.tableViewer.getTable().getItem(itemIndex);
 
@@ -285,7 +328,22 @@
 		} else {
 			assertEquals("", tableItem.getText(2));
 		}
+	}
 
+	private void assertLoadingScheme(TableItem tableItem, ISchemeInformation information) {
+		// check pojo
+		UiSchemeInformation uiInformation = (UiSchemeInformation) tableItem.getData();
+		assertFalse(uiInformation.checked);
+		assertEquals(information.getName(), uiInformation.getName());
+		assertEquals(information.getDescription(), uiInformation.getDescription());
+		assertEquals(IDEWorkbenchMessages.UrlHandlerPreferencePage_LoadingText,
+				uiInformation.getHandlerInstanceLocation());
+
+		// check UI
+		assertFalse(tableItem.getChecked());
+		assertEquals(information.getName(), tableItem.getText(0));
+		assertEquals(information.getDescription(), tableItem.getText(1));
+		assertEquals(IDEWorkbenchMessages.UrlHandlerPreferencePage_LoadingText, tableItem.getText(2));
 	}
 
 	private void assertHandlerTextForSelection(UriSchemeHandlerPreferencePage page, int selection, String text) {