/*
* Copyright 2014 Google Inc. All rights reserved.
*
* 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.polimi.zarathustra.webdriver;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.w3c.dom.Document;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.Uninterruptibles;
/**
* A local webdriver worker to run a browser on the local machine.
*/
public final class LocalWebdriverWorker {
private static final class MonitorThread extends Thread {
private final long timeout;
private boolean done = false;
private final Runnable stopWorkerThread;
private MonitorThread(int timeoutSeconds, Runnable stopWorkerThread) {
timeout = timeoutSeconds * 1000;
this.stopWorkerThread = stopWorkerThread;
}
public void done() {
done = true;
}
@Override
public void run() {
long deadline = new Date().getTime() + timeout;
while (true) {
Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);
if (done) {
return;
}
if (new Date(deadline).after(new Date())) {
stopWorkerThread.run();
return;
}
}
}
}
private static final int MAX_TIMEOUT_SECONDS = 120;
private static final int DEFAULT_HARD_LIMIT_TIMEOUT_SECONDS = MAX_TIMEOUT_SECONDS + 5;
private final int hardLimitTimeoutSeconds;
private WebDriver driver;
private final boolean useExplorer;
public LocalWebdriverWorker() {
this(false);
}
public LocalWebdriverWorker(boolean useExplorer) {
this(useExplorer, DEFAULT_HARD_LIMIT_TIMEOUT_SECONDS);
}
@VisibleForTesting
LocalWebdriverWorker(boolean useExplorer, int hardLimitTimeoutSeconds) {
this.useExplorer = useExplorer;
this.hardLimitTimeoutSeconds = hardLimitTimeoutSeconds;
resetDriver();
}
/**
* Returns a Document representation of the DOM at the provided url. Before navigating to the url,
* all cookies are deleted.
*
* @param url The full, absolute URL to fetch the DOM from.
* @return A well formatted Document.
*/
public Document getDocument(final String url) {
MonitorThread monitor = new MonitorThread(hardLimitTimeoutSeconds, new Runnable() {
@Override
public void run() {
driver.quit();
throw new TimeoutException("Timedout while retrieving DOM for " + url);
}
});
monitor.setDaemon(true);
driver.manage().deleteAllCookies();
driver.get(url);
String page = driver.getPageSource();
monitor.done();
Preconditions.checkNotNull(url);
return WebdriverHelper.getDom(page);
}
public Document getDocumentAndStoreSource(final String url, File outputDir, String fileName) {
MonitorThread monitor = new MonitorThread(hardLimitTimeoutSeconds, new Runnable() {
@Override
public void run() {
driver.quit();
throw new TimeoutException("Timedout while retrieving DOM for " + url);
}
});
monitor.setDaemon(true);
driver.manage().deleteAllCookies();
driver.get(url);
// TODO(claudio): handle download errors, truncated pages, weird redirects (e.g. opendns)
String page = driver.getPageSource();
storeHtmlSource(outputDir, fileName, page);
monitor.done();
Preconditions.checkNotNull(url);
return WebdriverHelper.getDom(page);
}
/**
* Closes down the webdriver browser.
*/
public void quit() {
driver.quit();
}
public void storeHtmlSource(File outputDir, String fileName, String page) {
try {
// Write "page" to file in the default folder (html_sources) */
File output = new File(outputDir + "/html_sources", fileName.replace(".dom", ".html"));
String completeName = output.getAbsolutePath();
BufferedWriter out = new BufferedWriter(new FileWriter(completeName));
StringBuffer contents = new StringBuffer();
contents.append(page);
out.write(contents.toString());
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Resets and create a new browser instance.
*/
public WebDriver resetDriver() {
if (useExplorer) {
DesiredCapabilities caps = DesiredCapabilities.internetExplorer();
caps.setCapability(CapabilityType.ForSeleniumServer.ENSURING_CLEAN_SESSION, true);
caps.setCapability("unexpectedAlertBehaviour", "accept");
caps.setCapability(CapabilityType.ACCEPT_SSL_CERTS, true);
driver = new InternetExplorerDriver(caps);
} else {
DesiredCapabilities caps = DesiredCapabilities.firefox();
caps.setCapability(CapabilityType.ForSeleniumServer.ENSURING_CLEAN_SESSION, true);
driver = new FirefoxDriver(caps);
}
driver.manage().timeouts().pageLoadTimeout(MAX_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// VERY IMPORTANT: wait some time after opening IE, or you'll run into issues.
Uninterruptibles.sleepUninterruptibly(5, TimeUnit.SECONDS);
return driver;
}
}