diff --git a/CHANGELOG.md b/CHANGELOG.md index 888dfbe5..4578991a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,10 @@ window (browserless). That window lives in the browser's implicit context, which Chrome below 145 won't address by id. Ferrum now detects it and exposes as `Ferrum::Context#implicit?`. It's never disposed, so `#reset` won't clear such a browser. [#578] [#627] +- `Browser.setDownloadBehavior` raised `Failed to find browser context for id` for a page in the browser's implicit + context, so connecting to a browser that came up with its own startup window and passing `:save_path` died on the + first page. `Ferrum::Target#context_id`, and with it `Ferrum::Page#context_id`, is now `nil` for that context, + which is how Chrome wants it addressed, by omitting `browserContextId` [#546] - Connecting to a remote browser with `:url` drops query string when probing `/json/version`, endpoint might answer 401 and Ferrum died with `undefined method 'host' for nil`. in case of parse issues `Ferrum::NoWebSocketUrlError` is raised. - Targets of a type `Ferrum::Contexts` doesn't track were left attached and paused by auto-attach for the lifetime diff --git a/lib/ferrum/context.rb b/lib/ferrum/context.rb index 0f2c3790..0f7c5123 100644 --- a/lib/ferrum/context.rb +++ b/lib/ferrum/context.rb @@ -126,7 +126,7 @@ def create_target # # @return [Target] def add_target(params:, session_id: nil) - new_target = Target.new(@client, session_id, params) + new_target = Target.new(@client, session_id, params, implicit: implicit?) # `put_if_absent` returns nil if added a new value or existing if there was one already target = @targets.put_if_absent(new_target.id, new_target) || new_target # on first iteration session_id may be nil, then if session is present here we must set it to the target diff --git a/lib/ferrum/downloads.rb b/lib/ferrum/downloads.rb index c6638a7d..c3f08fcb 100644 --- a/lib/ferrum/downloads.rb +++ b/lib/ferrum/downloads.rb @@ -62,8 +62,9 @@ def set_behavior(save_path:, behavior: :allow) raise ArgumentError unless VALID_BEHAVIOR.include?(behavior.to_sym) raise Error, "supply absolute path for `:save_path` option" unless Pathname.new(save_path.to_s).absolute? + options = { browserContextId: @page.context_id } if @page.context_id @page.command("Browser.setDownloadBehavior", - browserContextId: @page.context_id, + **Hash(options), downloadPath: save_path, behavior: behavior, eventsEnabled: true) diff --git a/lib/ferrum/target.rb b/lib/ferrum/target.rb index 299744a0..5fb97fc1 100644 --- a/lib/ferrum/target.rb +++ b/lib/ferrum/target.rb @@ -17,11 +17,12 @@ class Target attr_reader :options attr_accessor :session_id - def initialize(browser_client, session_id = nil, params = nil) + def initialize(browser_client, session_id = nil, params = nil, implicit: false) @page = nil @worker = nil @session_id = session_id @params = params + @implicit = implicit @browser_client = browser_client @options = browser_client.options end @@ -137,11 +138,14 @@ def parent_id @params["parentId"] end - # The id of the browser context this target belongs to. + # The id of the browser context this target belongs to, `nil` for the + # browser's implicit context, which Chrome doesn't let us address by id, + # see {Context#implicit?}. Commands scoped to a browser context target it + # by omitting `browserContextId`. # # @return [String, nil] def context_id - @params["browserContextId"] + @params["browserContextId"] unless @implicit end # Whether this target is a window/tab, i.e. was opened via diff --git a/sig/ferrum/page.rbs b/sig/ferrum/page.rbs index 6c32ae65..eb7b4bad 100644 --- a/sig/ferrum/page.rbs +++ b/sig/ferrum/page.rbs @@ -12,7 +12,7 @@ module Ferrum include Interceptable attr_accessor referrer: String? - attr_reader context_id: String + attr_reader context_id: String? attr_reader target_id: String attr_reader event: Utils::Event attr_reader tracing: Page::Tracing @@ -26,7 +26,7 @@ module Ferrum attr_reader client: (Client | SessionClient) @client: (Client | SessionClient) - @context_id: String + @context_id: String? @target_id: String @options: Browser::Options @frames: ::Concurrent::Map[String, Frame] @@ -41,7 +41,7 @@ module Ferrum @downloads: Downloads @referrer: String? - def initialize: ((Client | SessionClient) client, context_id: String, target_id: String, ?proxy: { host: String, port: ::Integer, user: String?, password: String? }?) -> void + def initialize: ((Client | SessionClient) client, context_id: String?, target_id: String, ?proxy: { host: String, port: ::Integer, user: String?, password: String? }?) -> void def context: () -> Context? diff --git a/sig/ferrum/target.rbs b/sig/ferrum/target.rbs index 2fc409f0..a8b908dc 100644 --- a/sig/ferrum/target.rbs +++ b/sig/ferrum/target.rbs @@ -10,11 +10,12 @@ module Ferrum @worker: Worker? @session_id: String? @params: Hash[String, untyped] + @implicit: bool @browser_client: Client @options: Browser::Options @client: (Client | SessionClient)? - def initialize: (Client browser_client, ?String? session_id, ?Hash[String, untyped]? params) -> void + def initialize: (Client browser_client, ?String? session_id, ?Hash[String, untyped]? params, ?implicit: bool) -> void def update: (Hash[String, untyped] params) -> Hash[String, untyped] diff --git a/spec/downloads_spec.rb b/spec/downloads_spec.rb index d7ced500..7cd33879 100644 --- a/spec/downloads_spec.rb +++ b/spec/downloads_spec.rb @@ -70,6 +70,24 @@ end end + context "with the browser's implicit context" do + it "saves an attachment" do + with_external_browser(incognito: false) do |url| + remote = Ferrum::Browser.new(url: url, base_url: base_url, save_path: save_path) + remote_page = remote.create_page + + expect(remote_page.context_id).to be_nil + + remote_page.downloads.wait { remote_page.go_to("/#{filename}") } + + expect(File.exist?("#{save_path}/#{filename}")).to be true + ensure + remote&.quit + FileUtils.rm_rf(save_path) + end + end + end + context "with local path" do let(:save_path) { "spec/tmp" }