All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.modules.extbrowser.NbDefaultUnixBrowserImpl Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.netbeans.modules.extbrowser;

import java.awt.EventQueue;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Paths;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.extexecution.ExecutionDescriptor;
import org.netbeans.api.extexecution.ExecutionService;
import org.netbeans.api.extexecution.ExternalProcessBuilder;
import org.netbeans.api.extexecution.input.InputProcessor;
import org.netbeans.api.extexecution.input.InputProcessors;
import org.netbeans.api.extexecution.input.LineProcessor;
import org.openide.util.Exceptions;
import org.openide.util.RequestProcessor;
import org.openide.windows.InputOutput;

/**
 * Basic support for default browser funcionality on Unix system,
 * currently using "xdg-open".
 * Note this class is not used for JDK 6 and up, for that purpose is used
 * build-in JDK mechanism (java.awt.Desktop#browse).
 *
 * @author Peter Zavadsky
 */
class NbDefaultUnixBrowserImpl extends ExtBrowserImpl {
    
    private static final Logger LOGGER = Logger.getLogger(NbDefaultUnixBrowserImpl.class.getName());

    private static final String XDG_OPEN_COMMAND = "xdg-open"; // NOI18N
    private static final String XDG_SETTINGS_COMMAND = "xdg-settings"; // NOI18N
    private static final String XBROWSER_COMMAND = "x-www-browser"; // NOI18N
    
    private static final RequestProcessor REQUEST_PROCESSOR = 
        new RequestProcessor( NbDefaultUnixBrowserImpl.class );

    private static final boolean XDG_OPEN_AVAILABLE;
    private static final boolean XDG_SETTINGS_AVAILABLE;
    private static final boolean XBROWSER_AVAILABLE;
    
    static {
        // XXX Lame check to find out whether the functionality is installed.
        // TODO Find some better way to ensure it is there.
        XDG_OPEN_AVAILABLE = new File("/usr/bin/" + XDG_OPEN_COMMAND).exists(); // NOI18N
        XDG_SETTINGS_AVAILABLE = new File("/usr/bin/" + XDG_SETTINGS_COMMAND).exists(); // NOI18N
        XBROWSER_AVAILABLE = new File("/usr/bin/" + XBROWSER_COMMAND).exists(); // NOI18N
    }
    
    static boolean isAvailable() {
        return XDG_OPEN_AVAILABLE || XBROWSER_AVAILABLE;
    }
    
    
    NbDefaultUnixBrowserImpl(ExtWebBrowser extBrowser) {
        super ();
        this.extBrowserFactory = extBrowser;
        if (ExtWebBrowser.getEM().isLoggable(Level.FINE)) {
            ExtWebBrowser.getEM().log(Level.FINE, "" + System.currentTimeMillis() + "NbDefaultUnixBrowserImpl created with factory: " + extBrowserFactory); // NOI18N
        }
    }

    @Override
    protected PrivateBrowserFamilyId detectPrivateBrowserFamilyId() {
        PrivateBrowserFamilyId browserFamilyId = detectBrowserFamily();
        if (browserFamilyId != null) {
            return browserFamilyId;
        }
        return super.detectPrivateBrowserFamilyId();
    }

    @CheckForNull
    private PrivateBrowserFamilyId detectBrowserFamily() {
        String browserIdent = detectDefaultWebBrowser();
        if (browserIdent == null
                && XBROWSER_AVAILABLE) {
            // fallback
            try {
                browserIdent = Paths.get("/usr/bin/x-www-browser").toRealPath().getFileName().toString().toLowerCase(Locale.US); // NOI18N
            } catch (IOException ex) {
                LOGGER.log(Level.INFO, "Could not detect browser", ex);
            }
        }
        if (browserIdent == null) {
            return null;
        }
        if (browserIdent.indexOf("chrome") != -1) { // NOI18N
            return PrivateBrowserFamilyId.CHROME;
        }
        if (browserIdent.indexOf("chromium") != -1) { // NOI18N
            return PrivateBrowserFamilyId.CHROMIUM;
        }
        if (browserIdent.indexOf("firefox") != -1) { // NOI18N
            return PrivateBrowserFamilyId.FIREFOX;
        }
        if (browserIdent.indexOf("opera") != -1) { // NOI18N
            return PrivateBrowserFamilyId.OPERA;
        }
        if (browserIdent.indexOf("mozilla") != -1) { // NOI18N
            return PrivateBrowserFamilyId.MOZILLA;
        }
        return null;
    }

    @Override
    protected void loadURLInBrowserInternal(URL url) {
        assert !EventQueue.isDispatchThread();
        if (ExtWebBrowser.getEM().isLoggable(Level.FINE)) {
            ExtWebBrowser.getEM().log(Level.FINE, "" + System.currentTimeMillis() + "NbDeaultUnixBrowserImpl.setUrl: " + url); // NOI18N
        }
        url = URLUtil.createExternalURL(url, false);
        String urlArg = url.toExternalForm();
        // prefer xdg-open, then x-www-browser
        String command = XDG_OPEN_AVAILABLE ? XDG_OPEN_COMMAND : XBROWSER_COMMAND;

        ProcessBuilder pb = new ProcessBuilder(new String[] { command, urlArg });
        try {
            Process p = pb.start();
            REQUEST_PROCESSOR.post(new ProcessWatcher(p));
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

    private static String detectDefaultWebBrowser() {
        // XXX hotfix for #233047
        // assert !EventQueue.isDispatchThread();
        // #233145
        if (!XDG_SETTINGS_AVAILABLE) {
            return null;
        }
        OutputProcessorFactory outputProcessorFactory = new OutputProcessorFactory();
        ExternalProcessBuilder processBuilder = new ExternalProcessBuilder(XDG_SETTINGS_COMMAND) // NOI18N
                .addArgument("get") // NOI18N
                .addArgument("default-web-browser"); // NOI18N
        ExecutionDescriptor silentDescriptor = new ExecutionDescriptor()
                .inputOutput(InputOutput.NULL)
                .inputVisible(false)
                .frontWindow(false)
                .showProgress(false)
                .outProcessorFactory(outputProcessorFactory);
        Future result = ExecutionService.newService(processBuilder, silentDescriptor, "Detecting default web browser") // NOI18N
                .run();
        try {
            result.get(10, TimeUnit.SECONDS);
        } catch (InterruptedException | ExecutionException | TimeoutException ex) {
            LOGGER.log(Level.INFO, null, ex);
        }
        String output = outputProcessorFactory.getOutput();
        if (output == null) {
            return null;
        }
        return output.toLowerCase(Locale.US);
    }

    private static final class ProcessWatcher implements Runnable {

        private final Process p;

        ProcessWatcher (Process p) {
            this.p = p;
        }

        public void run() {
            try {
                int exitValue = p.waitFor();
                if (exitValue != 0) {
                    StringBuilder sb = new StringBuilder();
                    InputStream is = p.getErrorStream();
                    try {
                        int curByte = 0;
                        while ((curByte = is.read()) != -1) {
                            sb.append((char)curByte);
                        }
                    } catch (IOException ex) {
                        Exceptions.printStackTrace(ex);
                    }
                    ExtWebBrowser.getEM().log(Level.WARNING, sb.toString()); // NOI18N
                }
            } catch (InterruptedException ex) {
                Exceptions.printStackTrace(ex);
            } finally {
                // XXX #155906 Cleanup of the finished process.
                cleanupProcess(p);
            }
        }
    } // ProcessWatcher

    private static void cleanupProcess(Process p) {
        closeStream(p.getOutputStream());
        closeStream(p.getInputStream());
        closeStream(p.getErrorStream());
        p.destroy();
    }

    private static void closeStream(Closeable stream) {
        try {
            stream.close();
        } catch (IOException ioe) {
            log(ioe);
        }
    }

    private static void log(Exception e) {
        Logger.getLogger(NbDefaultUnixBrowserImpl.class.getName()).log(Level.INFO, null, e);
    }

    static final class OutputProcessorFactory implements ExecutionDescriptor.InputProcessorFactory {

        private volatile String output;


        @Override
        public InputProcessor newInputProcessor(InputProcessor defaultProcessor) {
            return InputProcessors.bridge(new LineProcessor() {

                @Override
                public void processLine(String line) {
                    assert output == null : output + " :: " + line;
                    output = line;
                }

                @Override
                public void reset() {
                }

                @Override
                public void close() {
                }

            });
        }

        public String getOutput() {
            return output;
        }

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy