Package org.openqa.selenium.server.browserlaunchers

Source Code of org.openqa.selenium.server.browserlaunchers.FirefoxChromeLauncher

/*
* Copyright 2006 ThoughtWorks, Inc.
*
*  Licensed 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.openqa.selenium.server.browserlaunchers;


import org.apache.commons.logging.Log;
import org.mortbay.log.LogFactory;
import org.openqa.selenium.server.ApplicationRegistry;
import org.openqa.selenium.server.BrowserConfigurationOptions;
import org.openqa.selenium.server.RemoteControlConfiguration;
import org.openqa.selenium.server.browserlaunchers.locators.Firefox2or3Locator;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

public class FirefoxChromeLauncher extends AbstractBrowserLauncher {

    public static final String CHROME_URL = "chrome://killff/content/kill.html";
    private static Log LOGGER = LogFactory.getLog(FirefoxChromeLauncher.class);

    private File customProfileDir;
    private String[] cmdarray;
    private boolean closed = false;
    private BrowserInstallation browserInstallation;
    private Process process;

    private AsyncExecute shell = new AsyncExecute();

    private static boolean changeMaxConnections = false;

    public FirefoxChromeLauncher(RemoteControlConfiguration configuration, String sessionId) {
        this(configuration, sessionId, (String) null);
    }

    public FirefoxChromeLauncher(RemoteControlConfiguration configuration, String sessionId, String browserString) {
        this(configuration, sessionId,
                ApplicationRegistry.instance().browserInstallationCache().locateBrowserInstallation(
                        "chrome", browserString, new Firefox2or3Locator()));
    }

    public FirefoxChromeLauncher(RemoteControlConfiguration configuration, String sessionId, BrowserInstallation browserInstallation) {
        super(sessionId, configuration);

        this.browserInstallation = browserInstallation;

        shell.setLibraryPath(browserInstallation.libraryPath());
        // Set MOZ_NO_REMOTE in order to ensure we always get a new Firefox process
        // http://blog.dojotoolkit.org/2005/12/01/running-multiple-versions-of-firefox-side-by-side
        shell.setEnvironmentVariable("MOZ_NO_REMOTE", "1");
    }

   
    /* (non-Javadoc)
     * @see org.openqa.selenium.server.browserlaunchers.AbstractBrowserLauncher#launch(java.lang.String)
     */
    @Override
    protected void launch(String url) {
        launch(url, null);      
    }

    protected void launch(String url, BrowserConfigurationOptions config) {
        final String profilePath;
        final String homePage;
        String profile = "";

        try {
            homePage = new ChromeUrlConvert().convert(url);
            if (config != null) {
                profile = config.getProfile();
            }
            profilePath = makeCustomProfile(homePage, profile);
            populateCustomProfileDirectory(profilePath);

            LOGGER.info("Launching Firefox...");
            cmdarray = new String[]{browserInstallation.launcherFilePath(), "-profile", profilePath};
            shell.setCommandline(cmdarray);
            process = shell.asyncSpawn();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void populateCustomProfileDirectory(String profilePath) throws IOException {
        /*
        * The first time we launch Firefox with an empty profile directory,
    * Firefox will launch itself, populate the profile directory, then
    * kill/relaunch itself, so our process handle goes out of date.
    * So, the first time we launch Firefox, we'll start it up at an URL
    * that will immediately shut itself down.
    */
        cmdarray = new String[]{browserInstallation.launcherFilePath(), "-profile", profilePath, "-chrome", CHROME_URL};
        LOGGER.info("Preparing Firefox profile...");
        shell.setCommandline(cmdarray);
        shell.execute();
        waitForFullProfileToBeCreated(20 * 1000);
    }

    private String makeCustomProfile(String homePage, String profile) throws IOException {
        customProfileDir = LauncherUtils.createCustomProfileDir(sessionId);

        String sourceLocationName = "/customProfileDirCUSTFFCHROME";

        File firefoxProfileTemplate = null;
        if (!"".equals(getConfiguration().getProfilesLocation()) && !"".equals(profile)) {
            File profileDirectory = getConfiguration().getProfilesLocation();
            firefoxProfileTemplate = new File(profileDirectory + "/" + profile);
            if (!firefoxProfileTemplate.exists()) {
                throw new RuntimeException("The profile specified '" + firefoxProfileTemplate.getAbsolutePath() + "' does not exist");
            }
        } else  {
            firefoxProfileTemplate = getConfiguration().getFirefoxProfileTemplate();
        }
       
        if (firefoxProfileTemplate != null) {
            LauncherUtils.copyDirectory(firefoxProfileTemplate, customProfileDir);
        }
        ResourceExtractor.extractResourcePath(getClass(), sourceLocationName, customProfileDir);

        copyRunnerHtmlFiles();

        LauncherUtils.generatePacAndPrefJs(customProfileDir, getPort(), LauncherUtils.ProxySetting.NO_PROXY, homePage, changeMaxConnections, getConfiguration());

        return customProfileDir.getAbsolutePath();
    }


    private void copyRunnerHtmlFiles() {
        String guid = "{503A0CD4-EDC8-489b-853B-19E0BAA8F0A4}";
        File extensionDir = new File(customProfileDir, "extensions/" + guid);
        File htmlDir = new File(extensionDir, "chrome");
        htmlDir.mkdirs();

        LauncherUtils.extractHTAFile(htmlDir, getPort(), "/core/TestRunner.html", "TestRunner.html");
        LauncherUtils.extractHTAFile(htmlDir, getPort(), "/core/RemoteRunner.html", "RemoteRunner.html");

    }


    public void close() {
        if (closed) return;
        if (process == null) return;
        LOGGER.info("Killing Firefox...");
        Exception taskKillException = null;
        Exception fileLockException = null;
        if (false) {
            try {
                // try to kill with windows taskkill
                WindowsUtils.kill(cmdarray);
            } catch (Exception e) {
                taskKillException = e;
            }
        }
        int exitValue = AsyncExecute.killProcess(process);
        if (exitValue == 0) {
            LOGGER.warn("Firefox seems to have ended on its own (did we kill the real browser???)");
        }
        try {
            waitForFileLockToGoAway(5 * 000, 500);
        } catch (FileLockRemainedException e1) {
            fileLockException = e1;
        }


        try {
            LauncherUtils.deleteTryTryAgain(customProfileDir, 6);
        } catch (RuntimeException e) {
            if (taskKillException != null || fileLockException != null) {
                LOGGER.error("Couldn't delete custom Firefox profile directory", e);
                LOGGER.error("Perhaps caused by this exception:");
                if (taskKillException != null) LOGGER.error("Perhaps caused by this exception:", taskKillException);
                if (fileLockException != null) LOGGER.error("Perhaps caused by this exception:", fileLockException);
                throw new RuntimeException("Couldn't delete custom Firefox " +
                        "profile directory, presumably because task kill failed; " +
                        "see error LOGGER!", e);
            }
            throw e;
        }
        closed = true;
    }

    public Process getProcess() {
        return process;
    }

    /**
     * Firefox knows it's running by using a "parent.lock" file in
     * the profile directory.  Wait for this file to go away (and stay gone)
     *
     * @param timeout    max time to wait for the file to go away
     * @param timeToWait minimum time to wait to make sure the file is gone
     * @throws FileLockRemainedException
     */
    private void waitForFileLockToGoAway(long timeout, long timeToWait) throws FileLockRemainedException {
        File lock = new File(customProfileDir, "parent.lock");
        for (long start = System.currentTimeMillis(); System.currentTimeMillis() < start + timeout;) {
            AsyncExecute.sleepTight(500);
            if (!lock.exists() && makeSureFileLockRemainsGone(lock, timeToWait)) return;
        }
        if (lock.exists()) throw new FileLockRemainedException("Lock file still present! " + lock.getAbsolutePath());
    }

    /**
     * When initializing the profile, Firefox rapidly starts, stops, restarts and
     * stops again; we need to wait a bit to make sure the file lock is really gone.
     *
     * @param lock       the parent.lock file in the profile directory
     * @param timeToWait minimum time to wait to see if the file shows back
     *                   up again. This is not a timeout; we will always wait this amount of time or more.
     * @return true if the file stayed gone for the entire timeToWait; false if the
     *         file exists (or came back)
     */
    private boolean makeSureFileLockRemainsGone(File lock, long timeToWait) {
        for (long start = System.currentTimeMillis(); System.currentTimeMillis() < start + timeToWait;) {
            AsyncExecute.sleepTight(500);
            if (lock.exists()) return false;
        }
        return !lock.exists();
    }

    /**
     * Wait for one of the Firefox-generated files to come into existence, then wait
     * for Firefox to exit
     *
     * @param timeout the maximum amount of time to wait for the profile to be created
     */
    private void waitForFullProfileToBeCreated(long timeout) {
        // This will be a characteristic file in the profile
        File testFile = new File(customProfileDir, "extensions.ini");
        long start = System.currentTimeMillis();
        for (; System.currentTimeMillis() < start + timeout;) {

            AsyncExecute.sleepTight(500);
            if (testFile.exists()) break;
        }
        if (!testFile.exists()) throw new RuntimeException("Timed out waiting for profile to be created!");
        // wait the rest of the timeout for the file lock to go away
        long subTimeout = timeout - (System.currentTimeMillis() - start);
        try {
            waitForFileLockToGoAway(subTimeout, 500);
        } catch (FileLockRemainedException e) {
            throw new RuntimeException("Firefox refused shutdown while preparing a profile", e);
        }
    }

    public static void setChangeMaxConnections(boolean changeMaxConnections) {
        FirefoxChromeLauncher.changeMaxConnections = changeMaxConnections;
    }

    private class FileLockRemainedException extends Exception {
        FileLockRemainedException(String message) {
            super(message);
        }
    }

    public static class ChromeUrlConvert {
        public String convert(String httpUrl) throws MalformedURLException {
            String query = LauncherUtils.getQueryString(httpUrl);
            String file = new File(new URL(httpUrl).getPath()).getName();
            return "chrome://src/content/" + file + "?" + query;
        }
    }

    @Override // need to specify an absolute resultsUrl
    public void launchHTMLSuite(String suiteUrl, String browserURL, boolean multiWindow, String defaultLogLevel) {
        launch(LauncherUtils.getDefaultHTMLSuiteUrl(browserURL, suiteUrl, multiWindow, getPort(), defaultLogLevel), null);
    }
   
    @Override // need to specify an absolute driverUrl
    public void launchRemoteSession(String browserURL, boolean multiWindow, BrowserConfigurationOptions config) {
        launch(LauncherUtils.getDefaultRemoteSessionUrl(browserURL, sessionId, multiWindow, getPort()), config);
    }

}
TOP

Related Classes of org.openqa.selenium.server.browserlaunchers.FirefoxChromeLauncher

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.