Skip to content

Commit

Permalink
Simplify page management and attach to pages as early as possible
Browse files Browse the repository at this point in the history
  • Loading branch information
twalpole committed Feb 11, 2019
2 parents 886860c + dc43d51 commit 1f57ba0
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 230 deletions.
154 changes: 98 additions & 56 deletions lib/capybara/apparition/browser.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# frozen_string_literal: true

require 'capybara/apparition/errors'
require 'capybara/apparition/dev_tools_protocol/target_manager'
require 'capybara/apparition/page'
require 'capybara/apparition/console'
require 'capybara/apparition/dev_tools_protocol/session'
require 'capybara/apparition/browser/header'
require 'capybara/apparition/browser/window'
require 'capybara/apparition/browser/render'
Expand All @@ -30,7 +30,7 @@ class Browser
def initialize(client, logger = nil)
@client = client
@current_page_handle = nil
@targets = Capybara::Apparition::DevToolsProtocol::TargetManager.new(self)
@pages = {}
@context_id = nil
@js_errors = true
@ignore_https_errors = false
Expand All @@ -41,11 +41,8 @@ def initialize(client, logger = nil)
initialize_handlers

command('Target.setDiscoverTargets', discover: true)
while @current_page_handle.nil?
puts 'waiting for target...'
sleep 0.1
end
@context_id = current_target.context_id
yield self if block_given?
reset
end

def restart
Expand Down Expand Up @@ -81,24 +78,31 @@ def click_coordinates(x, y)
include Auth

def reset
current_page_targets = @targets.of_type('page').values

new_context_id = command('Target.createBrowserContext')['browserContextId']
new_target_response = client.send_cmd('Target.createTarget', url: 'about:blank', browserContextId: new_context_id)
current_pages = @pages.keys

current_page_targets.each do |target|
new_target_response = client.send_cmd('Target.createTarget', url: 'about:blank', browserContextId: new_context_id)
@pages.each do |id, page|
begin
client.send_cmd('Target.disposeBrowserContext', browserContextId: target.context_id).discard_result
client.send_cmd('Target.disposeBrowserContext', browserContextId: page.browser_context_id).discard_result
rescue WrongWorld
puts 'Unknown browserContextId'
end
@targets.delete(target.id)
@pages.delete(id)
end

new_target_id = new_target_response['targetId']

session_id = command('Target.attachToTarget', targetId: new_target_id)['sessionId']
session = Capybara::Apparition::DevToolsProtocol::Session.new(self, client, session_id)

@pages[new_target_id] = Page.create(self, session, new_target_id, new_context_id, ignore_https_errors: ignore_https_errors,
js_errors: js_errors, extensions: @extensions,
url_blacklist: @url_blacklist, url_whitelist: @url_whitelist) # .inherit(@info.delete('inherit'))
@pages[new_target_id].send(:main_frame).loaded!

timer = Capybara::Helpers.timer(expire_in: 10)
until @targets.get(new_target_id)&.page&.usable?
until @pages[new_target_id].usable?
if timer.expired?
puts 'Timedout waiting for reset'
raise TimeoutError.new('reset')
Expand All @@ -110,6 +114,45 @@ def reset
true
end

def refresh_pages(opener:)
new_pages = command('Target.getTargets')['targetInfos'].select do |ti|
(ti['openerId'] == opener.target_id) && (ti['type'] == 'page') && (ti['attached'] == false)
end
sessions = new_pages.map do |page|
target_id = page['targetId']
session_result = client.send_cmd('Target.attachToTarget', targetId: target_id)
[target_id, session_result]
end

sessions = sessions.map do |(target_id, session_result)|
session = Capybara::Apparition::DevToolsProtocol::Session.new(self, client, session_result.result['sessionId'])
[target_id, session]
end

sessions.each do |(target_id, session)|
session.async_commands 'Page.enable', 'Network.enable', 'Runtime.enable', 'Security.enable', 'DOM.enable'
end

# sessions.each do |(target_id, session_result)|
# session = Capybara::Apparition::DevToolsProtocol::Session.new(self, client, session_result.result['sessionId'])
sessions.each do |(target_id, session)|
page_options = { ignore_https_errors: ignore_https_errors, js_errors: js_errors,
url_blacklist: @url_blacklist, url_whitelist: @url_whitelist }
new_page = Page.create(self, session, target_id, opener.browser_context_id, page_options).inherit(opener)
@pages[target_id] = new_page
end

# new_pages.each do |page|
# target_id = page['targetId']
# session_id = command('Target.attachToTarget', targetId: target_id)['sessionId']
# session = Capybara::Apparition::DevToolsProtocol::Session.new(self, client, session_id)
# page_options = { ignore_https_errors: ignore_https_errors, js_errors: js_errors,
# url_blacklist: @url_blacklist, url_whitelist: @url_whitelist }
# new_page = Page.create(self, session, page['targetId'], opener.browser_context_id, page_options).inherit(opener)
# @pages[target_id] = new_page
# end
end

def resize(width, height, screen: nil)
current_page.set_viewport width: width, height: height, screen: screen
end
Expand All @@ -128,20 +171,22 @@ def network_traffic(type = nil)
def extensions=(filenames)
@extensions = filenames
Array(filenames).each do |name|
begin
current_page.command('Page.addScriptToEvaluateOnNewDocument', source: File.read(name))
rescue Errno::ENOENT
raise ::Capybara::Apparition::BrowserError.new('name' => "Unable to load extension: #{name}", 'args' => nil)
end
current_page(allow_nil: true)&.add_extension(name)
end
end

def url_whitelist=(whitelist)
current_page&.url_whitelist = whitelist
@url_whitelist = whitelist
@pages.each do |_id, page|
page.url_whitelist = whitelist
end
end

def url_blacklist=(blacklist)
current_page&.url_blacklist = blacklist
@url_blacklist = blacklist
@pages.each do |_id, page|
page.url_blacklist = blacklist
end
end

attr_writer :debug
Expand All @@ -167,8 +212,13 @@ def command_for_session(session_id, name, params)
raise
end

def current_page
current_target.page
def current_page(allow_nil: false)
@pages[@current_page_handle] || begin
puts "No current page: #{@current_page_handle} : #{caller}" if ENV['DEBUG']
@current_page_handle = nil
raise NoSuchWindowError unless allow_nil
@current_page_handle
end
end

def console_messages(type = nil)
Expand All @@ -177,15 +227,6 @@ def console_messages(type = nil)

private

def current_target
@targets.get(@current_page_handle) || begin
puts "No current page: #{@current_page_handle}"
puts caller
@current_page_handle = nil
raise NoSuchWindowError
end
end

def log(message)
@logger&.puts message if ENV['DEBUG']
end
Expand Down Expand Up @@ -257,35 +298,36 @@ def log(message)
# end

def initialize_handlers
@client.on 'Target.targetCreated' do |info|
puts "Target Created Info: #{info}" if ENV['DEBUG']
target_info = info['targetInfo']
if !@targets.target?(target_info['targetId'])
# @targets.add(target_info['targetId'], DevToolsProtocol::Target.new(self, target_info))
@targets.add(target_info['targetId'], target_info)
puts "**** Target Added #{info}" if ENV['DEBUG']
elsif ENV['DEBUG']
puts "Target already existed #{info}"
end
@current_page_handle ||= target_info['targetId'] if target_info['type'] == 'page'
end
# @client.on 'Target.targetCreated' do |info|
# byebug
# puts "Target Created Info: #{info}" if ENV['DEBUG']
# target_info = info['targetInfo']
# if !@pages.key?(target_info['targetId'])
# @pages.add(target_info['targetId'], target_info)
# puts "**** Target Added #{info}" if ENV['DEBUG']
# elsif ENV['DEBUG']
# puts "Target already existed #{info}"
# end
# @current_page_handle ||= target_info['targetId'] if target_info['type'] == 'page'
# end

@client.on 'Target.targetDestroyed' do |info|
puts "**** Target Destroyed Info: #{info}" if ENV['DEBUG']
@targets.delete(info['targetId'])
@pages.delete(info['targetId'])
end

@client.on 'Target.targetInfoChanged' do |info|
puts "**** Target Info Changed: #{info}" if ENV['DEBUG']
target_info = info['targetInfo']
target = @targets.get(target_info['targetId'])
if target
target.update(target_info)
else
puts '****No target for the info change- creating****' if ENV['DEBUG']
@targets.add(target_info['targetId'], target_info)
end
end
# @client.on 'Target.targetInfoChanged' do |info|
# byebug
# puts "**** Target Info Changed: #{info}" if ENV['DEBUG']
# target_info = info['targetInfo']
# page = @pages[target_info['targetId']]
# if page
# page.update(target_info)
# else
# puts '****No target for the info change- creating****' if ENV['DEBUG']
# @pages.add(target_info['targetId'], target_info)
# end
# end
end
end
end
4 changes: 2 additions & 2 deletions lib/capybara/apparition/browser/header.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def headers
end

def headers=(headers)
@targets.pages.each do |page|
@pages.each do |_id, page|
page.perm_headers = headers.dup
page.temp_headers = {}
page.temp_no_redirect_headers = {}
Expand All @@ -23,7 +23,7 @@ def add_headers(headers)

def add_header(header, permanent: true, **_options)
if permanent == true
@targets.pages.each do |page|
@pages.each do |_id, page|
page.perm_headers.merge! header
page.update_headers
end
Expand Down
39 changes: 18 additions & 21 deletions lib/capybara/apparition/browser/window.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,39 @@ def current_window_handle
end

def window_handles
@targets.window_handles
@pages.keys
end

def switch_to_window(handle)
target = @targets.get(handle)
unless target&.page
target = @targets.get(find_window_handle(handle))
page = @pages[handle]
unless page
page = @pages[find_window_handle(handle)]
warn 'Finding window by name, title, or url is deprecated, please use a block/proc ' \
'with Session#within_window/Session#switch_to_window instead.'
end
raise NoSuchWindowError unless target&.page
raise NoSuchWindowError unless page

target.page.wait_for_loaded
@current_page_handle = target.id
page.wait_for_loaded
@current_page_handle = page.target_id
end

def open_new_window
context_id = current_target.context_id
info = command('Target.createTarget', url: 'about:blank', browserContextId: context_id)
target_id = info['targetId']
target = DevToolsProtocol::Target.new(self, info.merge('type' => 'page', 'inherit' => current_page))
target.page # Ensure page object construction happens
begin
puts "Adding #{target_id} - #{target.info}" if ENV['DEBUG']
@targets.add(target_id, target)
rescue ArgumentError
puts 'Target already existed' if ENV['DEBUG']
end
context_id = current_page.browser_context_id
target_id = command('Target.createTarget', url: 'about:blank', browserContextId: context_id)['targetId']
session_id = command('Target.attachToTarget', targetId: target_id)['sessionId']
session = Capybara::Apparition::DevToolsProtocol::Session.new(self, client, session_id)
@pages[target_id] = Page.create(self, session, target_id, context_id, ignore_https_errors: ignore_https_errors, js_errors: js_errors,
url_whitelist: @url_whitelist, extensions: @extensions, url_blacklist: @url_blacklist).inherit(current_page(allow_nil: true))
@pages[target_id].send(:main_frame).loaded!
target_id
end

def close_window(handle)
@current_page_handle = nil if @current_page_handle == handle
win_target = @targets.delete(handle)
warn 'Window was already closed unexpectedly' if win_target.nil?
win_target&.close
page = @pages.delete(handle)

warn 'Window was already closed unexpectedly' unless page
command('Target.closeTarget', targetId: handle)
end
end

Expand Down
5 changes: 2 additions & 3 deletions lib/capybara/apparition/dev_tools_protocol/session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
module Capybara::Apparition
module DevToolsProtocol
class Session
attr_reader :browser, :connection, :target_id, :session_id
attr_reader :browser, :connection, :session_id

def initialize(browser, connection, target_id, session_id)
def initialize(browser, connection, session_id)
@browser = browser
@connection = connection
@target_id = target_id
@session_id = session_id
@handlers = []
end
Expand Down
Loading

0 comments on commit 1f57ba0

Please sign in to comment.