niklasvh / html2canvas

Screenshots with JavaScript
https://html2canvas.hertzen.com/
MIT License
30.59k stars 4.81k forks source link

Screen capture on Firefox 58 & IE11 fails with latest html2canvas.js #1476

Open sandeepnagra opened 6 years ago

sandeepnagra commented 6 years ago

Here's my javascript (basically I am trying to get a screenshot of full webpage and not just the page visible in viewport):

function genScreenshot () {
            var canvasImgContentDecoded;
            html2canvas(document.body).then(function (canvas) {
               window.canvasImgContentDecoded = canvas.toDataURL("image/png");
            });
          };
genScreenshot();

It works fine on Chrome 64+, Safari 11, Edge 16 but fails on Firefox with following error:

[remote server] sun.reflect.NativeConstructorAccessorImpl():-2:in `newInstance0': ReferenceError: html2canvas is not defined (Selenium::WebDriver::Error::JavascriptError)
Build info: version: '3.4.0', revision: 'unknown', time: 'unknown'
System info: host: '199-7-165-185', ip: 'XXX.XX.165.185', os.name: 'windows', os.arch: 'x86', os.version: '6.3', java.version: '1.8.0_161'
Driver info: org.openqa.selenium.firefox.FirefoxDriver
Capabilities [{moz:profile=C:\Windows\proxy\rust_mozprofile.dKwQmTLR0ARl, rotatable=false, timeouts={implicit=0.0, pageLoad=300000.0, script=30000.0}, pageLoadStrategy=normal, moz:headless=false, platform=ANY, moz:accessibilityChecks=false, acceptInsecureCerts=true, browserVersion=58.0, platformVersion=6.3, moz:processID=3892.0, browserName=firefox, javascriptEnabled=true, platformName=windows_nt, moz:webdriverClick=true}]
Session ID: 468f4f16-361b-4662-a91e-e4ddeeff1c67 (org.openqa.selenium.JavascriptException)
    from [remote server] sun.reflect.NativeConstructorAccessorImpl():-1:in `newInstance'
    from [remote server] sun.reflect.DelegatingConstructorAccessorImpl():-1:in `newInstance'
    from [remote server] java.lang.reflect.Constructor():-1:in `newInstance'
    from [remote server] org.openqa.selenium.remote.http.W3CHttpResponseCodec(W3CHttpResponseCodec.java):150:in `createException'
    from [remote server] org.openqa.selenium.remote.http.W3CHttpResponseCodec(W3CHttpResponseCodec.java):115:in `decode'
    from [remote server] org.openqa.selenium.remote.http.W3CHttpResponseCodec(W3CHttpResponseCodec.java):45:in `decode'
    from [remote server] org.openqa.selenium.remote.HttpCommandExecutor(HttpCommandExecutor.java):164:in `execute'
    from [remote server] org.openqa.selenium.remote.service.DriverCommandExecutor(DriverCommandExecutor.java):82:in `execute'
    from [remote server] org.openqa.selenium.remote.RemoteWebDriver(RemoteWebDriver.java):637:in `execute'
    from [remote server] org.openqa.selenium.remote.RemoteWebDriver(RemoteWebDriver.java):573:in `executeScript'
    from [remote server] sun.reflect.NativeMethodAccessorImpl():-2:in `invoke0'
    from [remote server] sun.reflect.NativeMethodAccessorImpl():-1:in `invoke'
    from [remote server] sun.reflect.DelegatingMethodAccessorImpl():-1:in `invoke'
    from [remote server] java.lang.reflect.Method():-1:in `invoke'
    from [remote server] org.openqa.selenium.support.events.EventFiringWebDriver$2(EventFiringWebDriver.java):104:in `invoke'
    from [remote server] com.sun.proxy.$Proxy4():-1:in `executeScript'
    from [remote server] org.openqa.selenium.support.events.EventFiringWebDriver(EventFiringWebDriver.java):217:in `executeScript'
    from [remote server] org.openqa.selenium.remote.server.handler.ExecuteScript(ExecuteScript.java):56:in `call'
    from [remote server] java.util.concurrent.FutureTask():-1:in `run'
    from [remote server] org.openqa.selenium.remote.server.DefaultSession$1(DefaultSession.java):176:in `run'
    from [remote server] java.util.concurrent.ThreadPoolExecutor():-1:in `runWorker'
    from [remote server] java.util.concurrent.ThreadPoolExecutor$Worker():-1:in `run'
    from [remote server] java.lang.Thread():-1:in `run'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/remote/response.rb:69:in `assert_ok'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/remote/response.rb:32:in `initialize'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/remote/http/common.rb:81:in `new'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/remote/http/common.rb:81:in `create_response'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/remote/http/default.rb:104:in `request'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/remote/http/common.rb:59:in `call'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/remote/bridge.rb:164:in `execute'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/remote/oss/bridge.rb:579:in `execute'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/remote/oss/bridge.rb:267:in `execute_script'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/common/driver.rb:211:in `execute_script'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/watir-6.8.1/lib/watir/browser.rb:265:in `execute_script'
    from /Users/mm/RubymineProjects/test/full_page_screenshots.rb:18:in `base64_canvas'
    from /Users/mm/RubymineProjects/test/full_page_screenshots.rb:87:in `<top (required)>'
    from -e:1:in `load'
    from -e:1:in `<main>'

Process finished with exit code 1

I get the following error for IE11:

[remote server] sun.reflect.NativeConstructorAccessorImpl():-2:in `newInstance0': JavaScript error (WARNING: The server did not provide any stacktrace information) (Selenium::WebDriver::Error::UnknownError)
Command duration or timeout: 47 milliseconds
Build info: version: '2.53.0', revision: '35ae25b', time: '2016-03-15 17:00:58'
System info: host: '207-254-11-32', ip: 'XXX.XX.11.32', os.name: 'windows', os.arch: 'x86', os.version: '6.3', java.version: '1.8.0_161'
Driver info: org.openqa.selenium.ie.InternetExplorerDriver
Capabilities [{browserAttachTimeout=0, enablePersistentHover=false, ie.forceCreateProcessApi=false, pageLoadStrategy=normal, ie.usePerProcessProxy=false, ignoreZoomSetting=false, handlesAlerts=true, version=11, platform=WINDOWS, nativeEvents=false, ie.ensureCleanSession=false, elementScrollBehavior=0, ie.browserCommandLineSwitches=, requireWindowFocus=false, browserName=internet explorer, initialBrowserUrl=about:blank, takesScreenshot=true, javascriptEnabled=true, ignoreProtectedModeSettings=false, enableElementCacheCleanup=true, cssSelectorsEnabled=true, unexpectedAlertBehaviour=dismiss}]
Session ID: e74a4b00-471b-47fc-b24a-5ff7023bbd2c (org.openqa.selenium.WebDriverException)
    from [remote server] sun.reflect.NativeConstructorAccessorImpl():-1:in `newInstance'
    from [remote server] sun.reflect.DelegatingConstructorAccessorImpl():-1:in `newInstance'
    from [remote server] java.lang.reflect.Constructor():-1:in `newInstance'
    from [remote server] org.openqa.selenium.remote.ErrorHandler(ErrorHandler.java):206:in `createThrowable'
    from [remote server] org.openqa.selenium.remote.ErrorHandler(ErrorHandler.java):158:in `throwIfResponseFailed'
    from [remote server] org.openqa.selenium.remote.RemoteWebDriver(RemoteWebDriver.java):678:in `execute'
    from [remote server] org.openqa.selenium.remote.RemoteWebDriver(RemoteWebDriver.java):577:in `executeScript'
    from [remote server] sun.reflect.NativeMethodAccessorImpl():-2:in `invoke0'
    from [remote server] sun.reflect.NativeMethodAccessorImpl():-1:in `invoke'
    from [remote server] sun.reflect.DelegatingMethodAccessorImpl():-1:in `invoke'
    from [remote server] java.lang.reflect.Method():-1:in `invoke'
    from [remote server] org.openqa.selenium.support.events.EventFiringWebDriver$2(EventFiringWebDriver.java):103:in `invoke'
    from [remote server] com.sun.proxy.$Proxy1():-1:in `executeScript'
    from [remote server] org.openqa.selenium.support.events.EventFiringWebDriver(EventFiringWebDriver.java):217:in `executeScript'
    from [remote server] org.openqa.selenium.remote.server.handler.ExecuteScript(ExecuteScript.java):56:in `call'
    from [remote server] java.util.concurrent.FutureTask():-1:in `run'
    from [remote server] org.openqa.selenium.remote.server.DefaultSession$1(DefaultSession.java):176:in `run'
    from [remote server] java.util.concurrent.ThreadPoolExecutor():-1:in `runWorker'
    from [remote server] java.util.concurrent.ThreadPoolExecutor$Worker():-1:in `run'
    from [remote server] java.lang.Thread():-1:in `run'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/remote/response.rb:69:in `assert_ok'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/remote/response.rb:32:in `initialize'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/remote/http/common.rb:81:in `new'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/remote/http/common.rb:81:in `create_response'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/remote/http/default.rb:104:in `request'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/remote/http/common.rb:59:in `call'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/remote/bridge.rb:164:in `execute'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/remote/oss/bridge.rb:579:in `execute'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/remote/oss/bridge.rb:267:in `execute_script'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/selenium-webdriver-3.8.0/lib/selenium/webdriver/common/driver.rb:211:in `execute_script'
    from /Users/mm/.rvm/gems/ruby-2.4.1/gems/watir-6.8.1/lib/watir/browser.rb:265:in `execute_script'
    from /Users/mm/RubymineProjects/test/full_page_screenshots.rb:18:in `base64_canvas'
    from /Users/mm/RubymineProjects/test/full_page_screenshots.rb:87:in `<top (required)>'
    from -e:1:in `load'
    from -e:1:in `<main>'

Process finished with exit code 1

Let me know if more information is required from my end.

Update: On IE11, I am getting full screenshot with just self.base64 command, so I am good with that. Only issue is Firefox.

BrunoMarc commented 6 years ago

How is it working in IE 11? Mine don't work at all, I didin't understand the "self.base64" command part, can you help me? Thanks!

sandeepnagra commented 6 years ago

@BrunoMarc - sorry for the late response. Here's the script I used for testing:

# #!/usr/bin/env ruby
require "watir"
require "base64"
require 'time'

MAX_SCREENSHOT_WAIT_TIME = 120

module Watir
  class Screenshot
    def base64_canvas(browser)
      @browser = browser
      output = nil

      return self.base64 if @browser&.name == :firefox || @browser&.name == :internet_explorer # reported bug - https://github.com/niklasvh/html2canvas/issues/1476

      @browser.execute_script html2canvas_payload
      sleep 3
      @browser.execute_script h2c_activator

      @browser.wait_until(timeout: MAX_SCREENSHOT_WAIT_TIME) {
        output = @browser.execute_script "return window.canvasImgContentDecoded;"
      }

      raise "Could not generate screenshot blob within #{MAX_SCREENSHOT_WAIT_TIME} seconds" unless output

      return output.sub!(/^data\:image\/png\;base64,/, '')
    end
    private
      def h2c_activator
        %<
          function genScreenshot () {
            var canvasImgContentDecoded;
            html2canvas(document.body).then(function (canvas) {
               window.canvasImgContentDecoded = canvas.toDataURL("image/png");
            });
          };
          genScreenshot();
        >.gsub(/\s+/, ' ').strip
      end

      def html2canvas_payload
        path = File.join(Dir.pwd, "vendor/html2canvas.js")
        File.read(path)
      end
  end
end

b = Watir::Browser.new :firefox

b.goto "https://www.massmutual.com/"

img = b.screenshot.base64_canvas(b)
png = Base64.decode64(img)
File.open('full-page-screenshot_with_canvas_redraw.png', 'wb') { |f| f.write(png) }

b.quit

For now I detect what browser I am executing the script for and if it is Firefox or IE, I just return self.base64.

With self.base64 Firefox returns image for just current viewport but IE weirdly returns for the whole webpage (blessing in disguise).

Let me know if this doesn't help.

~Sandeep