Closed
Bug 1471265
Opened 8 years ago
Closed 7 years ago
WebDriver:TakeScreenshot fails in canvas "scale()" for huge web pages
Categories
(Remote Protocol :: Marionette, defect, P3)
Tracking
(Not tracked)
RESOLVED
DUPLICATE
of bug 1485730
People
(Reporter: aclvaz, Unassigned)
References
()
Details
User Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36
Steps to reproduce:
GeckoDriver Version: 0.21.0
Platform: Windows 7 Enterprise Edition 64 bits
Firefox: 59.0.2 (64-bit)
Selenium: 3.12.1
Testcase
just load page from www.lwb.com or www.fitu.com and get a snapshot
The following code resulted from the need to deal with multiple issues when getting a random web page snapshot (pop ups, alert windows, pages that load several different resources, have incomplete loads, make redirects, etc), coming from Selenium 2 and using FireFox Webdriver (Firefox 45) **that could take a FULL window snapshot and not only the ViewPort**.
www.lwb.com, for instance, would get a snapshot with 1000 x 32766 px at 24 bits.
With Selenium 3 and Gecko Driver/Marionette one can only get the viewport (most of the time) and pages with big heights result in a Javascript error that propagates upwards thru the stack trace and is not easy to spot unless browsing thru the source code (at least on C# bindings).
I'm including the main code verbatim as a guideline to others that may face similar issues when taking snapshots.
private void CreateWebDriver()
{
FirefoxProfile customProfile = new FirefoxProfile();
customProfile.SetPreference("dom.disable_beforeunload", true); // disable want to leave page question.
customProfile.SetPreference("full-screen-api.approval-required", false); // disable maximize firefox question
customProfile.SetPreference("network.http.connection-timeout", 25); // connection time-out.
customProfile.SetPreference("dom.max_chrome_script_run_time", 0);
customProfile.SetPreference("dom.max_script_run_time", 0);
// for some pesky sites
customProfile.SetPreference("browser.download.folderList", 2);
customProfile.SetPreference("browser.download.dir", Directory.GetCurrentDirectory());
customProfile.SetPreference("browser.helperApps.neverAsk.saveToDisk", "text/vnd.wap.wml,application/x-trash,httpd/unix-directory,application/vnd.framemaker,application/octet-stream"); // ex: dgj.com, yld.com, zpo.com?, cwb.net, kuke.com
customProfile.SetPreference("media.volume_scale", "0.0");
customProfile.SetPreference("dom.successive_dialog_time_limit", 1);
FirefoxOptions fo = new FirefoxOptions();
fo.Profile = customProfile;
fo.SetPreference("security.sandbox.content.level", 5);
fo.AddAdditionalCapability("acceptInsecureCerts", true, true);
fo.AcceptInsecureCertificates = true;
fo.Profile.AcceptUntrustedCertificates = true;
fo.Profile.AssumeUntrustedCertificateIssuer = true;
fo.Profile.DeleteAfterUse = true;
fo.UnhandledPromptBehavior = UnhandledPromptBehavior.Dismiss;
fo.LogLevel = FirefoxDriverLogLevel.Trace;
fo.SetPreference("webdriver.log.file", "d:\\selenium\\driver.log");
_driver = new FirefoxDriver(FirefoxDriverService.CreateDefaultService(), fo, TimeSpan.FromSeconds(60));
_driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(40);
}
public Screenshot GenerateScreenshot(string url, int width, int height)
{
int retries = 0;
bool executed = false;
while (retries < 4 && !executed)
{
try
{
// Selenium relies on a try catch model heavily and there are a lot of situation were it can't
// get a correct response and consistent state when interacting with the driver and browser, namely when there is a command timeout.
// so we need to proceed with the processing to give more time for the browser page to update itself
// "clear" previous browsing
_driver.Navigate().GoToUrl("about:blank");
_driver.Manage().Window.Size = new Size(width, height);
string mainWindowHandle = _driver.CurrentWindowHandle;
// load page
try
{
_driver.Navigate().GoToUrl(url);
}
catch (WebDriverTimeoutException ex)
{
UpdateLog(String.Format("[{0}]WARNING: Timeout when getting " + url + " -> " + ex.Message, DateTime.Now));
}
// take some extra time to try to load things up
if (retries > 0) Thread.Sleep(retries * 20 * 1000);
// stop alerts AND basic auth login, after timeout on page load. stop multiple alert windows
IAlert alert;
while ((alert = SeleniumExtras.WaitHelpers.ExpectedConditions.AlertIsPresent().Invoke(_driver)) != null) {
//if (alert != null)
alert.Dismiss();
Thread.Sleep(500);
}
// stop pop-ups
foreach (string activeHandle in _driver.WindowHandles)
{
if (!activeHandle.Equals(mainWindowHandle))
{
_driver.SwitchTo().Window(activeHandle).Close();
// set focus back on main window
_driver.SwitchTo().Window(mainWindowHandle).SwitchTo();
}
}
// there is no bullet proof solution to get a complete page load so we will give always more X seconds for the page to load (ajax, flash, java, ...)
var javaScriptExecutor = _driver as IJavaScriptExecutor;
var waitPage = new WebDriverWait(_driver, TimeSpan.FromSeconds(10));
try
{
bool readyCondition(IWebDriver webDriver) => (bool)javaScriptExecutor.ExecuteScript("return (document.readyState == 'complete' && ((window.jQuery === undefined) || jQuery.active == 0))");
waitPage.Until(readyCondition);
}
catch (WebDriverTimeoutException ex)
{
UpdateLog(String.Format("[{0}]WARNING: Timeout when loading " + url + " -> " + ex.Message, DateTime.Now));
}
//width = Convert.ToInt32(((IJavaScriptExecutor)_driver).ExecuteScript("return document.body.scrollWidth"));
//height = Convert.ToInt32(((IJavaScriptExecutor)_driver).ExecuteScript("return document.body.scrollHeight"));
//_driver.Manage().Window.Size = new Size(width, height);
// wait for meta http-equiv redirects
System.Collections.ObjectModel.ReadOnlyCollection<IWebElement> metas = _driver.FindElements(By.XPath("//meta[@http-equiv='refresh']"));
if (metas.Count > 0)
{
IWebElement meta = metas[0];
string content = meta.GetAttribute("content");
content = content.Replace(" ", "");
int pos = content.IndexOf(';');
if (pos != -1 && pos != content.Length-1)
{
string timeout = content.Substring(0, pos);
string newURL = content.Substring(pos + 1).Substring(4);
// prevent an website asking for a refresh too long in the future
int iTimeout = Convert.ToInt32(timeout);
if (iTimeout <= 60)
{
//Thread.Sleep(Convert.ToInt32(timeout) * 1000);
WebDriverWait waitRedirect = new WebDriverWait(_driver, TimeSpan.FromSeconds(iTimeout + 1));
try
{
waitRedirect.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.UrlContains(newURL));
}
catch (WebDriverTimeoutException ex)
{
UpdateLog(String.Format("[{0}]WARNING: Timeout when waiting redirect from " + url + " to " + newURL + " -> " + ex.Message, DateTime.Now));
}
}
}
}
// always give an extra X seconds to load resources, like flash objects
Thread.Sleep(5000);
// if previous URL is the same as current URL then it might be the case where the browser hasn't
// yet updated the page => Wait another X seconds to avoid getting staled information when getting the snapshot
if (_driver.Url == "about:blank")
Thread.Sleep(10000);
executed = true;
}
catch (ThreadAbortException)
{
throw;
}
catch (Exception ex)
{
UpdateLog(String.Format("[{0}]WARNING: Exception when getting " + url + " -> " + ex.Message, DateTime.Now));
if (retries < 3)
{
QuitWebDriver();
CreateWebDriver();
UpdateLog(String.Format("[{0}]Going to retry...", DateTime.Now));
}
retries++;
}
}
if (!executed)
{
UpdateLog(String.Format("[{0}]ERROR: Giving up on " + url + " after {1} retries", DateTime.Now, retries - 1));
return null;
}
else
return ((ITakesScreenshot)_driver).GetScreenshot();
}
Actual results:
Stacktrace
on return ((ITakesScreenshot)_driver).GetScreenshot()
[24-06-2018 22:00:20]Starting...
[24-06-2018 22:00:58]Error on Snapshot 1 -> [Exception... "Failure" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: chrome://marionette/content/capture.js :: capture.canvas :: line 134" data: no]
Trace-level log
1529874013125 geckodriver INFO geckodriver 0.21.0
1529874013165 geckodriver INFO Listening on 127.0.0.1:52889
1529874014270 mozrunner::runner INFO Running command: "D:\program files (x86)\Mozilla Firefox\firefox.exe" "-marionette" "-foreground" "
-no-remote" "-profile" "D:\Users\Antonio\AppData\Local\Temp\rust_mozprofile.YSfJb1OrKtha"
1529874014278 geckodriver::marionette DEBUG Waiting 60s to connect to browser on 127.0.0.1:52893
1529874019757 geckodriver::marionette DEBUG Connected to Marionette on 127.0.0.1:52893
1529874020148 webdriver::server DEBUG <- 200 OK {"value": {"sessionId":"055b3f28-2ede-42b8-81f0-15a0137e906e","capabilities":{"acceptInsecur
eCerts":true,"browserName":"firefox","browserVersion":"59.0.2","moz:accessibilityChecks":false,"moz:headless":false,"moz:processID":11608,"moz:profile
":"D:\Users\Antonio\AppData\Local\Temp\rust_mozprofile.YSfJb1OrKtha","moz:useNonSpecCompliantPointerOrigin":false,"moz:webdriverClick":true,"pag
eLoadStrategy":"normal","platformName":"windows_nt","platformVersion":"6.1","rotatable":false,"timeouts":{"implicit":0,"pageLoad":300000,"script":3000
0}}}}
1529874020173 webdriver::server DEBUG -> POST /session/055b3f28-2ede-42b8-81f0-15a0137e906e/timeouts {"pageLoad":40000}
1529874020178 webdriver::server DEBUG <- 200 OK {"value": null}
1529874020471 webdriver::server DEBUG -> POST /session/055b3f28-2ede-42b8-81f0-15a0137e906e/url {"url":"about:blank"}
1529874020517 webdriver::server DEBUG <- 200 OK {"value": null}
1529874027353 webdriver::server DEBUG -> POST /session/055b3f28-2ede-42b8-81f0-15a0137e906e/window/rect {"width":1024,"height":768}
1529874027375 webdriver::server DEBUG <- 200 OK {"value": {"height":768,"width":1024,"x":4,"y":4}}
1529874028088 webdriver::server DEBUG -> GET /session/055b3f28-2ede-42b8-81f0-15a0137e906e/window
1529874028092 webdriver::server DEBUG <- 200 OK {"value":"4294967297"}
1529874029069 webdriver::server DEBUG -> POST /session/055b3f28-2ede-42b8-81f0-15a0137e906e/url {"url":"http://www.lwb.com"}
1529874030260 webdriver::server DEBUG <- 200 OK {"value": null}
1529874032193 webdriver::server DEBUG -> GET /session/055b3f28-2ede-42b8-81f0-15a0137e906e/alert/text
1529874032197 webdriver::server DEBUG <- 404 Not Found {"value":{"error":"no such alert","message":"No modal dialog is currently open","stac
ktrace":"WebDriverError@chrome://marionette/content/error.js:172:5\nNoAlertOpenError@chrome://marionette/content/error.js:393:5\nGeckoDriver.prototype
._checkIfAlertIsPresent@chrome://marionette/content/driver.js:3167:11\nGeckoDriver.prototype.getTextFromDialog@chrome://marionette/content/driver.js:3
130:3\ndespatch@chrome://marionette/content/server.js:557:20\nexecute@chrome://marionette/content/server.js:531:11\nonPacket/<@chrome://marionette/con
tent/server.js:506:15\nonPacket@chrome://marionette/content/server.js:505:8\n_onJSONObjectReady/<@chrome://marionette/content/transport.js:500:9\n"}}
1529874034100 webdriver::server DEBUG -> GET /session/055b3f28-2ede-42b8-81f0-15a0137e906e/window/handles
1529874034105 webdriver::server DEBUG <- 200 OK {"value":["4294967297"]}
1529874038068 webdriver::server DEBUG -> POST /session/055b3f28-2ede-42b8-81f0-15a0137e906e/execute/sync {"script":"return (document.readySt
ate == 'complete' && ((window.jQuery === undefined) || jQuery.active == 0))","args":[]}
1529874038081 webdriver::server DEBUG <- 200 OK {"value":true}
1529874038925 webdriver::server DEBUG -> POST /session/055b3f28-2ede-42b8-81f0-15a0137e906e/elements {"using":"xpath","value":"//meta[@http-
equiv='refresh']"}
1529874038939 webdriver::server DEBUG <- 200 OK {"value":[]}
1529874046146 webdriver::server DEBUG -> GET /session/055b3f28-2ede-42b8-81f0-15a0137e906e/url
1529874046155 webdriver::server DEBUG <- 200 OK {"value":"http://www.lwb.com/"}
1529874054179 webdriver::server DEBUG -> GET /session/055b3f28-2ede-42b8-81f0-15a0137e906e/screenshot
1529874054204 webdriver::server DEBUG <- 500 Internal Server Error {"value":{"error":"unknown error","message":"[Exception... "Failure" n
sresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: chrome://marionette/content/capture.js :: capture.canvas :: line 134" data: no]"
,"stacktrace":"capture.canvas@chrome://marionette/content/capture.js:134:3\ncapture.viewport@chrome://marionette/content/capture.js:71:10\ntakeScreens
hot@chrome://marionette/content/listener.js:1551:14\ndispatch/</req<@chrome://marionette/content/listener.js:491:14\ndispatch/<@chrome://marionette/co
ntent/listener.js:486:15\n"}}
Expected results:
should have returned a snapshot of the web page's viewport, at 1024x768, in a string encoded in base64.
Comment 1•8 years ago
|
||
Triaging this issue to testing::geeckodriver as it seems to me the correct component.
Component: Untriaged → geckodriver
Product: Firefox → Testing
So this was originally filed as https://github.com/mozilla/geckodriver/issues/1306. I had a quick look and wanted to also create a bug. Good to see this has already been done.
The failure actually happens when calling `scale()` on the canvas. I haven't had the time to further check that yet.
Status: UNCONFIRMED → NEW
Component: geckodriver → Marionette
Ever confirmed: true
Priority: -- → P3
Summary: WebDriver:TakeScreenshot generates error when web page has a big height → WebDriver:TakeScreenshot fails in canvas "scale()" for huge web pages
Hi,
Following the originally launched ticket (https://github.com/mozilla/geckodriver/issues/1306), this errors seems reproducible in FF 63.0.1. Is this the same bug?
1. Browse to http://www.lwb.com/ in FF
2. Click the Ellipses in the URL Nav bar
3. Click 'Take a Screenshot'
4. Click 'Save full page' (top right)
Firefox screencap will crash with this:
Unhandled error:
Exception
columnNumber: 0
data: null
filename: "moz-extension://20e29653-f702-444e-982c-8d5f0a559b75/selector/shooter.js"
line: const imageData = canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height);
lineNumber: 87
message: ""
name: "NS_ERROR_FAILURE"
result: 2147500037
stack: "screenshotPageAsync@moz-extension://20e29653-f702-444e-982c-8d5f0a559b75/selector/shooter.js:87:47\nexports.downloadShot@moz-extension://20e29653-f702-444e-982c-8d5f0a559b75/selector/shooter.js:182:76\ndownloadShot@moz-extension://20e29653-f702-444e-982c-8d5f0a559b75/selector/uicontrol.js:103:5\nonDownloadPreview@moz-extension://20e29653-f702-444e-982c-8d5f0a559b75/selector/uicontrol.js:218:7\nassertIsTrusted/<@moz-extension://20e29653-f702-444e-982c-8d5f0a559b75/assertIsTrusted.js:17:12\nwatchFunction/this.catcher@moz-extension://20e29653-f702-444e-982c-8d5f0a559b75/catcher.js:55:16\n"
Flags: needinfo?(dburns)
abk, thanks for the report but this is not for Marionette but an extension, or the internal screenshot feature of Firefox. If it is still happening for you with a recent Nightly or Beta build of Firefox, please file a new bug under https://bugzilla.mozilla.org/enter_bug.cgi?product=Firefox&component=Screenshots.
The Marionette bug as filed here has basically already fixed by bug 1485730.
Status: NEW → RESOLVED
Closed: 7 years ago
Flags: needinfo?(dburns)
Resolution: --- → DUPLICATE
thank you for the clarification, I will do some more testing and lodge a new ticket :) It is indeed the internal screenshot feature of Firefox (which is implemented as an extension). Its amusing that two very similar bugs (canvas size issues) cropped up in two similar places :)
A nice workaround is to load the target page in an iframe with limited height, then take a screenshot of the iframe, which seems to prevent the crash.
Updated•3 years ago
|
Product: Testing → Remote Protocol
You need to log in
before you can comment on or make changes to this bug.
Description
•