/******************************************************************************* * Copyright (c) 2005, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - Initial API and implementation * Wind River - Fix the issue that failing to launch browser with space in its path * Tomasz Zarna (Tasktop Technologies) - [429546] External Browser with parameters *******************************************************************************/ package org.eclipse.ui.internal.browser; import java.io.File; import java.net.URL; import java.util.ArrayList; import org.eclipse.jface.util.Util; import org.eclipse.osgi.util.NLS; import org.eclipse.ui.PartInitException; import org.eclipse.ui.browser.AbstractWebBrowser; import org.eclipse.ui.internal.browser.browsers.StreamConsumer; /** * An instance of a running Web browser. rundll32.exe * url.dll,FileProtocolHandler www.ibm.com */ public class ExternalBrowserInstance extends AbstractWebBrowser { protected IBrowserDescriptor browser; protected Process process; public ExternalBrowserInstance(String id, IBrowserDescriptor browser) { super(id); this.browser = browser; } @Override public void openURL(URL url) throws PartInitException { final String urlText = url == null ? null : url.toExternalForm(); ArrayList<String> cmdOptions = new ArrayList<>(); String location = browser.getLocation(); cmdOptions.add(location); String parameters = browser.getParameters(); /** * If true, then report non-zero exit values. Primarily useful when * using a launcher, like OS X's open(1), as some browsers (like IE) * routinely return non-zero values (bug 475775). */ final boolean reportNonZeroExitValue[] = new boolean[] { false }; Trace .trace( Trace.FINEST, "Launching external Web browser: " + location + " - " + parameters + " - " + urlText); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ // For MacOS X .app, we use open(1) to launch the app for the given URL // The order of the arguments is specific: // // open -a APP URL --args PARAMETERS // // As #createParameterArray() will append the URL to the end if %URL% // isn't found, we only include urlText if the parameters makes // reference to %URL%. This could mean that %URL% is specified // twice on the command line (e.g., "open -a XXX URL --args XXX URL // %%%") but presumably the user means to do that. boolean isMacBundle = Util.isMac() && isMacAppBundle(location); boolean includeUrlInParams = !isMacBundle || (parameters != null && parameters.contains(IBrowserDescriptor.URL_PARAMETER)); String[] params = WebBrowserUtil.createParameterArray(parameters, includeUrlInParams ? urlText : null); try { if (isMacBundle) { cmdOptions.add(0, "-a"); //$NON-NLS-1$ cmdOptions.add(0, "open"); //$NON-NLS-1$ if (urlText != null) { cmdOptions.add(urlText); } // --args supported in 10.6 and later if (params.length > 0) { cmdOptions.add("--args");//$NON-NLS-1$ } reportNonZeroExitValue[0] = true; } for (String param : params) { cmdOptions.add(param); } String[] cmd = cmdOptions.toArray(new String[cmdOptions.size()]); Trace.trace(Trace.FINEST, "Launching " + join(" ", cmd)); //$NON-NLS-1$//$NON-NLS-2$ process = Runtime.getRuntime().exec(cmd); Thread outConsumer = new StreamConsumer(process.getInputStream()); outConsumer.setName("External browser output reader"); //$NON-NLS-1$ outConsumer.start(); Thread errConsumer = new StreamConsumer(process.getErrorStream()); errConsumer.setName("External browser error reader"); //$NON-NLS-1$ errConsumer.start(); Thread thread = new Thread() { @Override public void run() { try { process.waitFor(); if (reportNonZeroExitValue[0] && process.exitValue() != 0) { Trace.trace(Trace.SEVERE, "External browser returned non-zero status: " + process.exitValue()); //$NON-NLS-1$ WebBrowserUtil.openError(NLS.bind(Messages.errorCouldNotLaunchExternalWebBrowser, urlText)); } DefaultBrowserSupport.getInstance().removeBrowser(ExternalBrowserInstance.this); } catch (Exception e) { // ignore } } }; thread.setDaemon(true); thread.start(); } catch (Exception e) { Trace.trace(Trace.SEVERE, "Could not launch external browser", e); //$NON-NLS-1$ WebBrowserUtil.openError(NLS.bind( Messages.errorCouldNotLaunchExternalWebBrowser, urlText)); } } /** * @return true if the location appears to be a Mac Application bundle * (.app) */ public static boolean isMacAppBundle(String location) { return isMacAppBundle(new File(location)); } /** * @return true if the location appears to be a Mac Application bundle * (.app) */ public static boolean isMacAppBundle(File location) { // A very quick heuristic based on Apple's Bundle Programming Guide // https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW19 File macosDir = new File(new File(location, "Contents"), "MacOS"); //$NON-NLS-1$ //$NON-NLS-2$ File plist = new File(new File(location, "Contents"), "Info.plist"); //$NON-NLS-1$ //$NON-NLS-2$ return location.isDirectory() && macosDir.isDirectory() && plist.isFile(); } private String join (String delim, String ... data) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < data.length; i++) { sb.append(data[i]); if (i >= data.length-1) {break;} sb.append(delim); } return sb.toString(); } @Override public boolean close() { try { process.destroy(); return true; } catch (Exception e) { return false; } } }