com.googlecode.fightinglayoutbugs.WebPage Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fighting-layout-bugs Show documentation
Show all versions of fighting-layout-bugs Show documentation
A library for automatic detection of layout bugs in web pages
/*
* Copyright 2009-2011 Michael Tamm
*
* 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 com.googlecode.fightinglayoutbugs;
import com.googlecode.fightinglayoutbugs.Screenshot.Condition;
import org.apache.commons.io.IOUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import javax.annotation.Nonnull;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.*;
/**
* Represents a web page. This class was created to improve
* performance when several {@link LayoutBugDetector}s
* analyze the same page. It caches as much information
* as possible.
*
* @author Michael Tamm
*/
public abstract class WebPage {
private TextDetector _textDetector;
private EdgeDetector _edgeDetector;
private String _url;
private String _html;
private final List _screenshots = new ArrayList();
private boolean[][] _textPixels;
private boolean[][] _horizontalEdges;
private boolean[][] _verticalEdges;
private boolean _jqueryInjected;
private boolean _textColorsBackedUp;
private String _currentTextColor;
/**
* Sets the detector for {@link #getTextPixels()},
* default is the {@link AnimationAwareTextDetector}.
*/
public void setTextDetector(TextDetector textDetector) {
if (_textPixels != null) {
throw new IllegalStateException("getTextPixels() was already called.");
}
_textDetector = textDetector;
}
/**
* Sets the detector for {@link #getHorizontalEdges()} and {@link #getVerticalEdges()},
* default is the {@link SimpleEdgeDetector}.
*/
public void setEdgeDetector(EdgeDetector edgeDetector) {
if (_horizontalEdges != null) {
throw new IllegalStateException("getHorizontalEdges() was already called.");
}
if (_verticalEdges != null) {
throw new IllegalStateException("getVerticalEdges() was already called.");
}
_edgeDetector = edgeDetector;
}
public void resizeBrowserWindowTo(Dimension newBrowserWindowSize) {
Number currentBrowserWindowWidth = (Number) executeJavaScript("return window.outerWidth");
if (currentBrowserWindowWidth == null || currentBrowserWindowWidth.intValue() != newBrowserWindowSize.width || ((Number) executeJavaScript("return window.outerHeight")).intValue() != newBrowserWindowSize.height) {
executeJavaScript("window.resizeTo(" + newBrowserWindowSize.width + ", " + newBrowserWindowSize.height + ")");
if (currentBrowserWindowWidth != null) {
// Check if resizing succeeded ...
int browserWindowWidth = ((Number) executeJavaScript("return window.outerWidth")).intValue();
int browserWindowHeight = ((Number) executeJavaScript("return window.outerHeight")).intValue();
if (browserWindowWidth != newBrowserWindowSize.width || browserWindowHeight != newBrowserWindowSize.height) {
throw new RuntimeException("Failed to resize browser window. If you are using Chrome, see http://code.google.com/p/chromium/issues/detail?id=2091");
}
}
// Clear all cached screenshots and derived values ...
_screenshots.clear();
_textPixels = null;
_horizontalEdges = null;
_verticalEdges = null;
}
}
/**
* Returns the URL of this web page.
*/
public String getUrl() {
if (_url == null) {
_url = retrieveUrl();
}
return _url;
}
/**
* Returns the source HTML of this web page.
*/
public String getHtml() {
if (_html == null) {
_html = retrieveHtml();
}
return _html;
}
private class Unmodified implements Condition {
public boolean isSatisfiedBy(Screenshot screenshot) {
return screenshot.textColor == null;
}
public boolean satisfyWillModifyWebPage() {
return false;
}
public void satisfyFor(WebPage webPage) {
restoreTextColors();
}
}
/**
* Returns a screenshot of this web page.
*
* @param conditions conditions the taken screenshot must satisfy.
*/
public Screenshot getScreenshot(Condition... conditions) {
// 1.) Check if there is a condition, which modifies the web page ...
boolean thereIsAConditionWhichModifiesTheWebPage = false;
for (int i = 0; i < conditions.length && !thereIsAConditionWhichModifiesTheWebPage; ++i) {
if (conditions[i].satisfyWillModifyWebPage()) {
thereIsAConditionWhichModifiesTheWebPage = true;
}
}
// 2.) If there is no condition, which modifies the web page, we need to add the Unmodified condition ...
if (!thereIsAConditionWhichModifiesTheWebPage) {
Condition[] temp = new Condition[1 + conditions.length];
temp[0] = new Unmodified();
System.arraycopy(conditions, 0, temp, 1, conditions.length);
conditions = temp;
}
// 3.) Check if we have already taken a screenshot which satisfies all conditions ...
for (Screenshot screenshot : _screenshots) {
boolean satisfiesAllConditions = true;
for (int i = 0; i < conditions.length && satisfiesAllConditions; ++i) {
if (!conditions[i].isSatisfiedBy(screenshot)) {
satisfiesAllConditions = false;
}
}
if (satisfiesAllConditions) {
return screenshot;
}
}
// 4.) No screenshot satisfied all conditions, we need to tak a new one ...
for (Condition condition : conditions) {
condition.satisfyFor(this);
}
return takeScreenshot();
}
/**
* Returns a two dimensional array a, whereby a[x][y] is true
* if the pixel with the coordinates x,y in a {@link #getScreenshot screenshot} of this web page
* belongs to displayed text, otherwise a[x][y] is false.
*/
public boolean[][] getTextPixels() {
if (_textPixels == null) {
if (_textDetector == null) {
_textDetector = new AnimationAwareTextDetector();
}
_textPixels = _textDetector.detectTextPixelsIn(this);
}
return _textPixels;
}
public Collection getRectangularRegionsCoveredBy(Collection jQuerySelectors) {
if (jQuerySelectors.isEmpty()) {
return Collections.emptySet();
}
injectJQueryIfNotPresent();
// 1.) Assemble JavaScript to select elements ...
Iterator i = jQuerySelectors.iterator();
String js = "jQuery('" + i.next().replace("'", "\\'");
while (i.hasNext()) {
js += "').add('" + i.next().replace("'", "\\'");
}
js += "')";
// 2.) Assemble JavaScript function to fill an array with rectangular region of each selected element ...
js = "function() { " +
"var a = new Array(); " +
js + ".each(function(i, e) { " +
"var j = jQuery(e); " +
"var o = j.offset(); " +
"a.push({ top: o.top, left: o.left, width: j.width(), height: j.height() }); " +
"}); " +
"return a; " +
"}";
// 3.) Execute JavaScript function ...
@SuppressWarnings("unchecked")
List
© 2015 - 2024 Weber Informatics LLC | Privacy Policy