puppeteer-ruby 0.40.3 → 0.40.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b55d70dfff58f262d0385b5cb3c30b14d61dee0b4e62b6159240fd4b108da11
4
- data.tar.gz: a61ebc455d7149b1e20422ab86545c7aea4753ce41bfe27c8dead388b9595ba2
3
+ metadata.gz: 51b26b6b3710f06e7a7cfb7e9f5730bbb9517396fd30a7f271161820bb6902c6
4
+ data.tar.gz: 528fd6520a029cf9d85424a105cf19a156f428435dc795a8b8753c8cbfa442db
5
5
  SHA512:
6
- metadata.gz: b58e74d4397fa2954df3a5ef90f3e3dc55702e058b2a5a0c84dff1cb236d012019a1f98626972ec3d11afb70708e760846ece034ec8e7dcf41bac1ded218c53c
7
- data.tar.gz: 315fdc4a2e7743bf21daf38e260a0c8876c77d8d19ee81797b083ddbb7628a1287e618ee812311995d270e5ee95b7febd4b1ca8e43deea0e33ade601b2b361dd
6
+ metadata.gz: ad616ae1574e76f71cb7e7cb1dae5cba4ae4ad65865ab6a5e6cb92c6033b9ce654de2b364b5d5351e69302f88fc48231c2a9bd97765af9ed827862e762f9d748
7
+ data.tar.gz: 9a194d8df09c6c6eda97ef095d67dafd693b7ff386e612d926925bdbcf41b3b2a21c06862eff90525e39f85c7d8a5ded419bb8c4b4a3ecb33413c7ccdf47215e
data/CHANGELOG.md CHANGED
@@ -1,7 +1,23 @@
1
- ### main [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.40.3...main)]
1
+ ### main [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.40.6...main)]
2
2
 
3
3
  - xxx
4
4
 
5
+ ### 0.40.6 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.40.5...0.40.6)]
6
+
7
+ - Port Puppeteer v13.1-v13.5 features mainly for request interception.
8
+
9
+ ### 0.40.5 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.40.4...0.40.5)]
10
+
11
+ Bugfix:
12
+
13
+ - Port Puppeteer v13.1-v13.5 bugfixes mainly for OOPIFs.
14
+
15
+ ### 0.40.4 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.40.3...0.40.4)]
16
+
17
+ Bugfix:
18
+
19
+ - Fix warning on handling console message.
20
+
5
21
  ### 0.40.3 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.40.2...0.40.3)]
6
22
 
7
23
  Bugfix:
data/docs/api_coverage.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # API coverages
2
- - Puppeteer version: v13.0.1
3
- - puppeteer-ruby version: 0.40.3
2
+ - Puppeteer version: v13.5.2
3
+ - puppeteer-ruby version: 0.40.6
4
4
 
5
5
  ## Puppeteer
6
6
 
@@ -204,7 +204,7 @@
204
204
 
205
205
  * args
206
206
  * location
207
- * ~~stackTrace~~
207
+ * stackTrace => `#stack_trace`
208
208
  * text
209
209
  * ~~type~~
210
210
 
@@ -310,8 +310,8 @@
310
310
  * frame
311
311
  * headers
312
312
  * initiator
313
- * ~~interceptResolutionState~~
314
- * ~~isInterceptResolutionHandled~~
313
+ * interceptResolutionState => `#intercept_resolution_state`
314
+ * isInterceptResolutionHandled => `#intercept_resolution_handled?`
315
315
  * isNavigationRequest => `#navigation_request?`
316
316
  * method
317
317
  * postData => `#post_data`
@@ -337,6 +337,7 @@
337
337
  * status
338
338
  * statusText => `#status_text`
339
339
  * text
340
+ * ~~timing~~
340
341
  * url
341
342
 
342
343
  ## ~~SecurityDetails~~
@@ -12,13 +12,23 @@ class Puppeteer::ConsoleMessage
12
12
  # @param log_type [String]
13
13
  # @param text [String]
14
14
  # @param args [Array<Puppeteer::JSHandle>]
15
- # @param location [Location]
16
- def initialize(log_type, text, args, location)
15
+ # @param stack_trace_locations [Array<Location>]
16
+ def initialize(log_type, text, args, stack_trace_locations)
17
17
  @log_type = log_type
18
18
  @text = text
19
19
  @args = args
20
- @location = location
20
+ @stack_trace_locations = stack_trace_locations
21
21
  end
22
22
 
23
- attr_reader :log_type, :text, :args, :location
23
+ attr_reader :log_type, :text, :args
24
+
25
+ # @return [Location]
26
+ def location
27
+ @stack_trace_locations.first
28
+ end
29
+
30
+ # @return [Array<Location>]
31
+ def stack_trace
32
+ @stack_trace_locations
33
+ end
24
34
  end
@@ -262,6 +262,58 @@ Puppeteer::DEVICES = Hash[
262
262
  isLandscape: true,
263
263
  },
264
264
  },
265
+ {
266
+ name: 'iPad (gen 6)',
267
+ userAgent:
268
+ 'Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
269
+ viewport: {
270
+ width: 768,
271
+ height: 1024,
272
+ deviceScaleFactor: 2,
273
+ isMobile: true,
274
+ hasTouch: true,
275
+ isLandscape: false,
276
+ },
277
+ },
278
+ {
279
+ name: 'iPad (gen 6) landscape',
280
+ userAgent:
281
+ 'Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
282
+ viewport: {
283
+ width: 1024,
284
+ height: 768,
285
+ deviceScaleFactor: 2,
286
+ isMobile: true,
287
+ hasTouch: true,
288
+ isLandscape: true,
289
+ },
290
+ },
291
+ {
292
+ name: 'iPad (gen 7)',
293
+ userAgent:
294
+ 'Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
295
+ viewport: {
296
+ width: 810,
297
+ height: 1080,
298
+ deviceScaleFactor: 2,
299
+ isMobile: true,
300
+ hasTouch: true,
301
+ isLandscape: false,
302
+ },
303
+ },
304
+ {
305
+ name: 'iPad (gen 7) landscape',
306
+ userAgent:
307
+ 'Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
308
+ viewport: {
309
+ width: 1080,
310
+ height: 810,
311
+ deviceScaleFactor: 2,
312
+ isMobile: true,
313
+ hasTouch: true,
314
+ isLandscape: true,
315
+ },
316
+ },
265
317
  {
266
318
  name: 'iPad Mini',
267
319
  userAgent:
@@ -314,6 +366,32 @@ Puppeteer::DEVICES = Hash[
314
366
  isLandscape: true,
315
367
  },
316
368
  },
369
+ {
370
+ name: 'iPad Pro 11',
371
+ userAgent:
372
+ 'Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
373
+ viewport: {
374
+ width: 834,
375
+ height: 1194,
376
+ deviceScaleFactor: 2,
377
+ isMobile: true,
378
+ hasTouch: true,
379
+ isLandscape: false,
380
+ },
381
+ },
382
+ {
383
+ name: 'iPad Pro 11 landscape',
384
+ userAgent:
385
+ 'Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
386
+ viewport: {
387
+ width: 1194,
388
+ height: 834,
389
+ deviceScaleFactor: 2,
390
+ isMobile: true,
391
+ hasTouch: true,
392
+ isLandscape: true,
393
+ },
394
+ },
317
395
  {
318
396
  name: 'iPhone 4',
319
397
  userAgent:
@@ -678,6 +756,214 @@ Puppeteer::DEVICES = Hash[
678
756
  isLandscape: true,
679
757
  },
680
758
  },
759
+ {
760
+ name: 'iPhone 12',
761
+ userAgent:
762
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
763
+ viewport: {
764
+ width: 390,
765
+ height: 844,
766
+ deviceScaleFactor: 3,
767
+ isMobile: true,
768
+ hasTouch: true,
769
+ isLandscape: false,
770
+ },
771
+ },
772
+ {
773
+ name: 'iPhone 12 landscape',
774
+ userAgent:
775
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
776
+ viewport: {
777
+ width: 844,
778
+ height: 390,
779
+ deviceScaleFactor: 3,
780
+ isMobile: true,
781
+ hasTouch: true,
782
+ isLandscape: true,
783
+ },
784
+ },
785
+ {
786
+ name: 'iPhone 12 Pro',
787
+ userAgent:
788
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
789
+ viewport: {
790
+ width: 390,
791
+ height: 844,
792
+ deviceScaleFactor: 3,
793
+ isMobile: true,
794
+ hasTouch: true,
795
+ isLandscape: false,
796
+ },
797
+ },
798
+ {
799
+ name: 'iPhone 12 Pro landscape',
800
+ userAgent:
801
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
802
+ viewport: {
803
+ width: 844,
804
+ height: 390,
805
+ deviceScaleFactor: 3,
806
+ isMobile: true,
807
+ hasTouch: true,
808
+ isLandscape: true,
809
+ },
810
+ },
811
+ {
812
+ name: 'iPhone 12 Pro Max',
813
+ userAgent:
814
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
815
+ viewport: {
816
+ width: 428,
817
+ height: 926,
818
+ deviceScaleFactor: 3,
819
+ isMobile: true,
820
+ hasTouch: true,
821
+ isLandscape: false,
822
+ },
823
+ },
824
+ {
825
+ name: 'iPhone 12 Pro Max landscape',
826
+ userAgent:
827
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
828
+ viewport: {
829
+ width: 926,
830
+ height: 428,
831
+ deviceScaleFactor: 3,
832
+ isMobile: true,
833
+ hasTouch: true,
834
+ isLandscape: true,
835
+ },
836
+ },
837
+ {
838
+ name: 'iPhone 12 Mini',
839
+ userAgent:
840
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
841
+ viewport: {
842
+ width: 375,
843
+ height: 812,
844
+ deviceScaleFactor: 3,
845
+ isMobile: true,
846
+ hasTouch: true,
847
+ isLandscape: false,
848
+ },
849
+ },
850
+ {
851
+ name: 'iPhone 12 Mini landscape',
852
+ userAgent:
853
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
854
+ viewport: {
855
+ width: 812,
856
+ height: 375,
857
+ deviceScaleFactor: 3,
858
+ isMobile: true,
859
+ hasTouch: true,
860
+ isLandscape: true,
861
+ },
862
+ },
863
+ {
864
+ name: 'iPhone 13',
865
+ userAgent:
866
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
867
+ viewport: {
868
+ width: 390,
869
+ height: 844,
870
+ deviceScaleFactor: 3,
871
+ isMobile: true,
872
+ hasTouch: true,
873
+ isLandscape: false,
874
+ },
875
+ },
876
+ {
877
+ name: 'iPhone 13 landscape',
878
+ userAgent:
879
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
880
+ viewport: {
881
+ width: 844,
882
+ height: 390,
883
+ deviceScaleFactor: 3,
884
+ isMobile: true,
885
+ hasTouch: true,
886
+ isLandscape: true,
887
+ },
888
+ },
889
+ {
890
+ name: 'iPhone 13 Pro',
891
+ userAgent:
892
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
893
+ viewport: {
894
+ width: 390,
895
+ height: 844,
896
+ deviceScaleFactor: 3,
897
+ isMobile: true,
898
+ hasTouch: true,
899
+ isLandscape: false,
900
+ },
901
+ },
902
+ {
903
+ name: 'iPhone 13 Pro landscape',
904
+ userAgent:
905
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
906
+ viewport: {
907
+ width: 844,
908
+ height: 390,
909
+ deviceScaleFactor: 3,
910
+ isMobile: true,
911
+ hasTouch: true,
912
+ isLandscape: true,
913
+ },
914
+ },
915
+ {
916
+ name: 'iPhone 13 Pro Max',
917
+ userAgent:
918
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
919
+ viewport: {
920
+ width: 428,
921
+ height: 926,
922
+ deviceScaleFactor: 3,
923
+ isMobile: true,
924
+ hasTouch: true,
925
+ isLandscape: false,
926
+ },
927
+ },
928
+ {
929
+ name: 'iPhone 13 Pro Max landscape',
930
+ userAgent:
931
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
932
+ viewport: {
933
+ width: 926,
934
+ height: 428,
935
+ deviceScaleFactor: 3,
936
+ isMobile: true,
937
+ hasTouch: true,
938
+ isLandscape: true,
939
+ },
940
+ },
941
+ {
942
+ name: 'iPhone 13 Mini',
943
+ userAgent:
944
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
945
+ viewport: {
946
+ width: 375,
947
+ height: 812,
948
+ deviceScaleFactor: 3,
949
+ isMobile: true,
950
+ hasTouch: true,
951
+ isLandscape: false,
952
+ },
953
+ },
954
+ {
955
+ name: 'iPhone 13 Mini landscape',
956
+ userAgent:
957
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
958
+ viewport: {
959
+ width: 812,
960
+ height: 375,
961
+ deviceScaleFactor: 3,
962
+ isMobile: true,
963
+ hasTouch: true,
964
+ isLandscape: true,
965
+ },
966
+ },
681
967
  {
682
968
  name: 'JioPhone 2',
683
969
  userAgent:
@@ -1133,6 +1419,84 @@ Puppeteer::DEVICES = Hash[
1133
1419
  isLandscape: true,
1134
1420
  },
1135
1421
  },
1422
+ {
1423
+ name: 'Pixel 4a (5G)',
1424
+ userAgent:
1425
+ 'Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4812.0 Mobile Safari/537.36',
1426
+ viewport: {
1427
+ width: 353,
1428
+ height: 745,
1429
+ deviceScaleFactor: 3,
1430
+ isMobile: true,
1431
+ hasTouch: true,
1432
+ isLandscape: false,
1433
+ },
1434
+ },
1435
+ {
1436
+ name: 'Pixel 4a (5G) landscape',
1437
+ userAgent:
1438
+ 'Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4812.0 Mobile Safari/537.36',
1439
+ viewport: {
1440
+ width: 745,
1441
+ height: 353,
1442
+ deviceScaleFactor: 3,
1443
+ isMobile: true,
1444
+ hasTouch: true,
1445
+ isLandscape: true,
1446
+ },
1447
+ },
1448
+ {
1449
+ name: 'Pixel 5',
1450
+ userAgent:
1451
+ 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4812.0 Mobile Safari/537.36',
1452
+ viewport: {
1453
+ width: 393,
1454
+ height: 851,
1455
+ deviceScaleFactor: 3,
1456
+ isMobile: true,
1457
+ hasTouch: true,
1458
+ isLandscape: false,
1459
+ },
1460
+ },
1461
+ {
1462
+ name: 'Pixel 5 landscape',
1463
+ userAgent:
1464
+ 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4812.0 Mobile Safari/537.36',
1465
+ viewport: {
1466
+ width: 851,
1467
+ height: 393,
1468
+ deviceScaleFactor: 3,
1469
+ isMobile: true,
1470
+ hasTouch: true,
1471
+ isLandscape: true,
1472
+ },
1473
+ },
1474
+ {
1475
+ name: 'Moto G4',
1476
+ userAgent:
1477
+ 'Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4812.0 Mobile Safari/537.36',
1478
+ viewport: {
1479
+ width: 360,
1480
+ height: 640,
1481
+ deviceScaleFactor: 3,
1482
+ isMobile: true,
1483
+ hasTouch: true,
1484
+ isLandscape: false,
1485
+ },
1486
+ },
1487
+ {
1488
+ name: 'Moto G4 landscape',
1489
+ userAgent:
1490
+ 'Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4812.0 Mobile Safari/537.36',
1491
+ viewport: {
1492
+ width: 640,
1493
+ height: 360,
1494
+ deviceScaleFactor: 3,
1495
+ isMobile: true,
1496
+ hasTouch: true,
1497
+ isLandscape: true,
1498
+ },
1499
+ },
1136
1500
  ].map do |json|
1137
1501
  [
1138
1502
  json[:name],
@@ -2,12 +2,13 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
2
2
  class BoxModel
3
3
  QUAD_ATTRIBUTE_NAMES = %i(content padding border margin)
4
4
  # @param result [Hash]
5
- def initialize(result_model)
5
+ # @param offset [Point]
6
+ def initialize(result_model, offset:)
6
7
  QUAD_ATTRIBUTE_NAMES.each do |attr_name|
7
8
  quad = result_model[attr_name.to_s]
8
9
  instance_variable_set(
9
10
  :"@#{attr_name}",
10
- quad.each_slice(2).map { |x, y| Point.new(x: x, y: y) },
11
+ quad.each_slice(2).map { |x, y| Point.new(x: x, y: y) + offset },
11
12
  )
12
13
  end
13
14
  @width = result_model['width']
@@ -11,10 +11,12 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
11
11
  # @param context [Puppeteer::ExecutionContext]
12
12
  # @param client [Puppeteer::CDPSession]
13
13
  # @param remote_object [Puppeteer::RemoteObject]
14
+ # @param frame [Puppeteer::Frame]
14
15
  # @param page [Puppeteer::Page]
15
16
  # @param frame_manager [Puppeteer::FrameManager]
16
- def initialize(context:, client:, remote_object:, page:, frame_manager:)
17
+ def initialize(context:, client:, remote_object:, frame:, page:, frame_manager:)
17
18
  super(context: context, client: client, remote_object: remote_object)
19
+ @frame = frame
18
20
  @page = page
19
21
  @frame_manager = frame_manager
20
22
  @disposed = false
@@ -134,6 +136,37 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
134
136
  end
135
137
  end
136
138
 
139
+ class ElementNotClickableError < StandardError
140
+ def initialize
141
+ super("Node is either not clickable or not an HTMLElement")
142
+ end
143
+ end
144
+
145
+ # @param quad [Array<Array<Point>>]]
146
+ # @param offset [Point]
147
+ private def apply_offsets_to_quad(quad, offset)
148
+ quad.map { |part| part + offset }
149
+ end
150
+
151
+ # @param frame [Puppeteer::Frame]
152
+ # @return [Point]
153
+ private def oopif_offsets(frame)
154
+ offset = Point.new(x: 0, y: 0)
155
+ while frame.parent_frame
156
+ parent = frame.parent_frame
157
+ unless frame.oop_frame?
158
+ frame = parent
159
+ next
160
+ end
161
+ backend_node_id = parent._client.send_message('DOM.getFrameOwner', frameId: frame.id)['backendNodeId']
162
+ result = parent._client.send_message('DOM.getBoxModel', backendNodeId: backend_node_id)
163
+ break unless result
164
+ offset = BoxModel.new(result['model'], offset: offset).content.first
165
+ frame = parent
166
+ end
167
+ offset
168
+ end
169
+
137
170
  def clickable_point(offset = nil)
138
171
  offset_param = Offset.from(offset)
139
172
 
@@ -150,12 +183,22 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
150
183
  end
151
184
 
152
185
  # Filter out quads that have too small area to click into.
153
- layout_metrics = @client.send_message('Page.getLayoutMetrics')
154
- client_width = layout_metrics["layoutViewport"]["clientWidth"]
155
- client_height = layout_metrics["layoutViewport"]["clientHeight"]
186
+ layout_metrics = @page.client.send_message('Page.getLayoutMetrics')
187
+
188
+ if result.empty? || result["quads"].empty?
189
+ raise ElementNotClickableError.new
190
+ end
191
+
192
+ # Filter out quads that have too small area to click into.
193
+ # Fallback to `layoutViewport` in case of using Firefox.
194
+ layout_viewport = layout_metrics["cssLayoutViewport"] || layout_metrics["layoutViewport"]
195
+ client_width = layout_viewport["clientWidth"]
196
+ client_height = layout_viewport["clientHeight"]
156
197
 
198
+ oopif_offset = oopif_offsets(@frame)
157
199
  quads = result["quads"].
158
200
  map { |quad| from_protocol_quad(quad) }.
201
+ map { |quad| apply_offsets_to_quad(quad, oopif_offset) }.
159
202
  map { |quad| intersect_quad_with_viewport(quad, client_width, client_height) }.
160
203
  select { |quad| compute_quad_area(quad) > 1 }
161
204
  if quads.empty?
@@ -357,13 +400,14 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
357
400
  # @return [BoundingBox|nil]
358
401
  def bounding_box
359
402
  if_present(box_model) do |result_model|
403
+ offset = oopif_offsets(@frame)
360
404
  quads = result_model.border
361
405
 
362
406
  x = quads.map(&:x).min
363
407
  y = quads.map(&:y).min
364
408
  BoundingBox.new(
365
- x: x,
366
- y: y,
409
+ x: x + offset.x,
410
+ y: y + offset.y,
367
411
  width: quads.map(&:x).max - x,
368
412
  height: quads.map(&:y).max - y,
369
413
  )
@@ -373,7 +417,7 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
373
417
  # @return [BoxModel|nil]
374
418
  def box_model
375
419
  if_present(@remote_object.box_model(@client)) do |result|
376
- BoxModel.new(result['model'])
420
+ BoxModel.new(result['model'], offset: oopif_offsets(@frame))
377
421
  end
378
422
  end
379
423
 
@@ -96,6 +96,7 @@ module FrameManagerEmittedEvents ; end
96
96
  FrameAttached: EventsDefinitionUtils.symbol('FrameManager.FrameAttached'),
97
97
  FrameNavigated: EventsDefinitionUtils.symbol('FrameManager.FrameNavigated'),
98
98
  FrameDetached: EventsDefinitionUtils.symbol('FrameManager.FrameDetached'),
99
+ FrameSwapped: EventsDefinitionUtils.symbol('FrameManager.FrameSwapped'),
99
100
  LifecycleEvent: EventsDefinitionUtils.symbol('FrameManager.LifecycleEvent'),
100
101
  FrameNavigatedWithinDocument: EventsDefinitionUtils.symbol('FrameManager.FrameNavigatedWithinDocument'),
101
102
  ExecutionContextCreated: EventsDefinitionUtils.symbol('FrameManager.ExecutionContextCreated'),
@@ -178,7 +178,7 @@ class Puppeteer::FrameManager
178
178
  frame = @frames[event['targetInfo']['targetId']]
179
179
  session = Puppeteer::Connection.from_session(@client).session(event['sessionId'])
180
180
 
181
- frame.send(:update_client, session)
181
+ frame&.send(:update_client, session)
182
182
  setup_listeners(session)
183
183
  async_init(session)
184
184
  end
@@ -353,6 +353,8 @@ class Puppeteer::FrameManager
353
353
  if frame
354
354
  remove_frame_recursively(frame)
355
355
  end
356
+ elsif reason == 'swap'
357
+ emit_event(FrameManagerEmittedEvents::FrameSwapped, frame)
356
358
  end
357
359
  end
358
360
 
@@ -2,6 +2,8 @@ class Puppeteer::HTTPRequest
2
2
  include Puppeteer::DebugPrint
3
3
  include Puppeteer::IfPresent
4
4
 
5
+ DEFAULT_INTERCEPT_RESOLUTION_PRIORITY = 0
6
+
5
7
  # defines some methods used only in NetworkManager, Response
6
8
  class InternalAccessor
7
9
  def initialize(request)
@@ -38,6 +40,43 @@ class Puppeteer::HTTPRequest
38
40
  end
39
41
  end
40
42
 
43
+ class InterceptResolutionState
44
+ def self.abort(priority: nil)
45
+ new(action: 'abort', priority: priority)
46
+ end
47
+
48
+ def self.respond(priority: nil)
49
+ new(action: 'respond', priority: priority)
50
+ end
51
+
52
+ def self.continue(priority: nil)
53
+ new(action: 'continue', priority: priority)
54
+ end
55
+
56
+ def self.disabled(priority: nil)
57
+ new(action: 'disabled', priority: priority)
58
+ end
59
+
60
+ def self.none(priority: nil)
61
+ new(action: 'none', priority: priority)
62
+ end
63
+
64
+ def self.already_handled(priority: nil)
65
+ new(action: 'already-handled', priority: priority)
66
+ end
67
+
68
+ private def initialize(action:, priority:)
69
+ @action = action
70
+ @priority = priority
71
+ end
72
+
73
+ def priority_unspecified?
74
+ @priority.nil?
75
+ end
76
+
77
+ attr_reader :action, :priority
78
+ end
79
+
41
80
  # @param client [Puppeteer::CDPSession]
42
81
  # @param frame [Puppeteer::Frame]
43
82
  # @param interception_id [string|nil]
@@ -57,9 +96,8 @@ class Puppeteer::HTTPRequest
57
96
  @frame = frame
58
97
  @redirect_chain = redirect_chain
59
98
  @continue_request_overrides = {}
60
- @current_strategy = 'none'
61
- @current_priority = nil
62
- @intercept_actions = []
99
+ @intercept_resolution_state = InterceptResolutionState.none
100
+ @intercept_handlers = []
63
101
  @initiator = event['initiator']
64
102
 
65
103
  @headers = {}
@@ -115,19 +153,29 @@ class Puppeteer::HTTPRequest
115
153
  @abort_error_reason
116
154
  end
117
155
 
118
- # @returns An array of the current intercept resolution strategy and priority
119
- # `[strategy,priority]`. Strategy is one of: `abort`, `respond`, `continue`,
120
- # `disabled`, `none`, or `already-handled`.
121
- def intercept_resolution
156
+ # @returns An InterceptResolutionState object describing the current resolution
157
+ # action and priority.
158
+ #
159
+ # InterceptResolutionState contains:
160
+ # action: InterceptResolutionAction
161
+ # priority?: number
162
+ #
163
+ # InterceptResolutionAction is one of: `abort`, `respond`, `continue`,
164
+ # `disabled`, `none`, or `alreay-handled`
165
+ def intercept_resolution_state
122
166
  if !@allow_interception
123
- ['disabled']
167
+ InterceptResolutionState.disabled
124
168
  elsif @interception_handled
125
- ['already-handled']
169
+ InterceptResolutionState.already_handled
126
170
  else
127
- [@current_strategy, @current_priority]
171
+ @intercept_resolution_state.dup
128
172
  end
129
173
  end
130
174
 
175
+ def intercept_resolution_handled?
176
+ @interception_handled
177
+ end
178
+
131
179
  # Adds an async request handler to the processing queue.
132
180
  # Deferred handlers are not guaranteed to execute in any particular order,
133
181
  # but they are guarnateed to resolve before the request interception
@@ -135,19 +183,19 @@ class Puppeteer::HTTPRequest
135
183
  #
136
184
  # @param pending_handler [Proc]
137
185
  def enqueue_intercept_action(pending_handler)
138
- @intercept_actions << pending_handler
186
+ @intercept_handlers << pending_handler
139
187
  end
140
188
 
141
189
  # Awaits pending interception handlers and then decides how to fulfill
142
190
  # the request interception.
143
191
  def finalize_interceptions
144
- @intercept_actions.each(&:call)
145
- case @intercept_resolution
146
- when :abort
192
+ @intercept_handlers.each(&:call)
193
+ case @intercept_resolution.action
194
+ when 'abort'
147
195
  abort_impl(**@abort_error_reason)
148
- when :respond
196
+ when 'respond'
149
197
  respond_impl(**@response_for_request)
150
- when :continue
198
+ when 'continue'
151
199
  continue_impl(@continue_request_overrides)
152
200
  end
153
201
  end
@@ -220,17 +268,16 @@ class Puppeteer::HTTPRequest
220
268
  end
221
269
 
222
270
  @continue_request_overrides = overrides
223
- if @current_priority.nil? || priority > @current_priority
224
- @current_strategy = :continue
225
- @current_priority = priority
271
+ if @intercept_resolution_state.priority_unspecified? || priority > @intercept_resolution_state.priority
272
+ @intercept_resolution_state = InterceptResolutionState.continue(priority: priority)
226
273
  return
227
274
  end
228
275
 
229
- if priority == @current_priority
230
- if @current_strategy == :abort || @current_strategy == :respond
276
+ if priority == @intercept_resolution_state.priority
277
+ if @intercept_resolution_state.action == :abort || @intercept_resolution_state.action == :respond
231
278
  return
232
279
  end
233
- @current_strategy = :continue
280
+ @intercept_resolution_state = InterceptResolutionState.continue(priority: priority)
234
281
  end
235
282
  end
236
283
 
@@ -284,17 +331,16 @@ class Puppeteer::HTTPRequest
284
331
  body: body,
285
332
  }
286
333
 
287
- if @current_priority.nil? || priority > @current_priority
288
- @current_strategy = :respond
289
- @current_priority = priority
334
+ if @intercept_resolution_state.priority_unspecified? || priority > @intercept_resolution_state.priority
335
+ @intercept_resolution_state = InterceptResolutionState.respond(priority: priority)
290
336
  return
291
337
  end
292
338
 
293
- if priority == @current_priority
294
- if @current_strategy == :abort
339
+ if priority == @intercept_resolution_state.priority
340
+ if @intercept_resolution_state.action == :abort
295
341
  return
296
342
  end
297
- @current_strategy = :respond
343
+ @intercept_resolution_state = InterceptResolutionState.respond(priority: priority)
298
344
  end
299
345
  end
300
346
 
@@ -360,9 +406,8 @@ class Puppeteer::HTTPRequest
360
406
  end
361
407
  @abort_error_reason = error_reason
362
408
 
363
- if @current_priority.nil? || priority > @current_priority
364
- @current_strategy = :abort
365
- @current_priority = priority
409
+ if @intercept_resolution_state.priority_unspecified? || priority > @intercept_resolution_state.priority
410
+ @intercept_resolution_state = InterceptResolutionState.abort(priority: priority)
366
411
  end
367
412
  end
368
413
 
@@ -12,6 +12,7 @@ class Puppeteer::JSHandle
12
12
  context: context,
13
13
  client: context.client,
14
14
  remote_object: remote_object,
15
+ frame: frame,
15
16
  page: frame_manager.page,
16
17
  frame_manager: frame_manager,
17
18
  )
@@ -77,6 +77,7 @@ class Puppeteer::LifecycleWatcher
77
77
  check_lifecycle_complete
78
78
  end,
79
79
  @frame_manager.add_event_listener(FrameManagerEmittedEvents::FrameNavigatedWithinDocument, &method(:navigated_within_document)),
80
+ @frame_manager.add_event_listener(FrameManagerEmittedEvents::FrameSwapped, &method(:handle_frame_swapped)),
80
81
  @frame_manager.add_event_listener(FrameManagerEmittedEvents::FrameDetached, &method(:handle_frame_detached)),
81
82
  ]
82
83
  @listener_ids['network_manager'] = @frame_manager.network_manager.add_event_listener(NetworkManagerEmittedEvents::Request, &method(:handle_request))
@@ -142,11 +143,21 @@ class Puppeteer::LifecycleWatcher
142
143
  check_lifecycle_complete
143
144
  end
144
145
 
146
+ private def handle_frame_swapped(frame)
147
+ return if frame != @frame
148
+ @swapped = true
149
+ check_lifecycle_complete
150
+ end
151
+
145
152
  private def check_lifecycle_complete
146
153
  # We expect navigation to commit.
147
154
  return unless @expected_lifecycle.completed?(@frame)
148
155
  @lifecycle_promise.fulfill(true) if @lifecycle_promise.pending?
149
156
  if @frame.loader_id == @initial_loader_id && !@has_same_document_navigation
157
+ if @swapped
158
+ @swapped = false
159
+ @new_document_navigation_promise.fulfill(true)
160
+ end
150
161
  return
151
162
  end
152
163
  if @has_same_document_navigation && @same_document_navigation_promise.pending?
@@ -217,6 +217,7 @@ class Puppeteer::NetworkManager
217
217
  # CDP may have sent a Fetch.requestPaused event already. Check for it.
218
218
  if_present(@network_event_manager.get_request_paused(network_request_id)) do |request_paused_event|
219
219
  fetch_request_id = request_paused_event['requestId']
220
+ patch_request_event_headers(event, request_paused_event)
220
221
  handle_request(event, fetch_request_id)
221
222
  @network_event_manager.forget_request_paused(network_request_id)
222
223
  end
@@ -277,12 +278,19 @@ class Puppeteer::NetworkManager
277
278
  end
278
279
 
279
280
  if request_will_be_sent_event
281
+ patch_request_event_headers(request_will_be_sent_event, event)
280
282
  handle_request(request_will_be_sent_event, fetch_request_id)
281
283
  else
282
284
  @network_event_manager.store_request_paused(network_request_id, event)
283
285
  end
284
286
  end
285
287
 
288
+ private def patch_request_event_headers(request_will_be_sent_event, request_paused_event)
289
+ request_will_be_sent_event['request']['headers'].merge!(
290
+ # includes extra headers, like: Accept, Origin
291
+ request_paused_event['request']['headers'])
292
+ end
293
+
286
294
  private def handle_request(event, fetch_request_id)
287
295
  redirect_chain = []
288
296
  if event['redirectResponse']
@@ -227,7 +227,7 @@ class Puppeteer::Page
227
227
  @client.send_message('Emulation.setGeolocationOverride', geolocation.to_h)
228
228
  end
229
229
 
230
- attr_reader :javascript_enabled, :target
230
+ attr_reader :javascript_enabled, :target, :client
231
231
  alias_method :javascript_enabled?, :javascript_enabled
232
232
 
233
233
  def browser
@@ -389,7 +389,18 @@ class Puppeteer::Page
389
389
  @client.send_message('Network.getCookies', urls: (urls.empty? ? [url] : urls))['cookies']
390
390
  end
391
391
 
392
+ # check if each cookie element has required fields ('name' and 'value')
393
+ private def assert_cookie_params(cookies, requires:)
394
+ return if cookies.all? do |cookie|
395
+ requires.all? { |field_name| cookie[field_name] || cookie[field_name.to_s] }
396
+ end
397
+
398
+ raise ArgumentError.new("Each coookie must have #{requires.join(" and ")} attribute.")
399
+ end
400
+
392
401
  def delete_cookie(*cookies)
402
+ assert_cookie_params(cookies, requires: %i(name))
403
+
393
404
  page_url = url
394
405
  starts_with_http = page_url.start_with?("http")
395
406
  cookies.each do |cookie|
@@ -399,12 +410,14 @@ class Puppeteer::Page
399
410
  end
400
411
 
401
412
  def set_cookie(*cookies)
413
+ assert_cookie_params(cookies, requires: %i(name value))
414
+
402
415
  page_url = url
403
416
  starts_with_http = page_url.start_with?("http")
404
417
  items = cookies.map do |cookie|
405
418
  (starts_with_http ? { url: page_url } : {}).merge(cookie).tap do |item|
406
419
  raise ArgumentError.new("Blank page can not have cookie \"#{item[:name]}\"") if item[:url] == "about:blank"
407
- raise ArgumetnError.new("Data URL page can not have cookie \"#{item[:name]}\"") if item[:url]&.start_with?("data:")
420
+ raise ArgumentError.new("Data URL page can not have cookie \"#{item[:name]}\"") if item[:url]&.start_with?("data:")
408
421
  end
409
422
  end
410
423
  delete_cookie(*items)
@@ -582,18 +595,19 @@ class Puppeteer::Page
582
595
  private def add_console_message(type, args, stack_trace)
583
596
  text_tokens = args.map { |arg| arg.remote_object.value }
584
597
 
585
- call_frame = stack_trace['callFrames']&.first
586
- location =
587
- if call_frame
588
- Puppeteer::ConsoleMessage::Location.new(
589
- url: call_frame['url'],
590
- line_number: call_frame['lineNumber'],
591
- column_number: call_frame['columnNumber'],
592
- )
598
+ stack_trace_locations =
599
+ if stack_trace && stack_trace['callFrames']
600
+ stack_trace['callFrames'].map do |call_frame|
601
+ Puppeteer::ConsoleMessage::Location.new(
602
+ url: call_frame['url'],
603
+ line_number: call_frame['lineNumber'],
604
+ column_number: call_frame['columnNumber'],
605
+ )
606
+ end
593
607
  else
594
- nil
608
+ []
595
609
  end
596
- console_message = Puppeteer::ConsoleMessage.new(type, text_tokens.join(' '), args, location)
610
+ console_message = Puppeteer::ConsoleMessage.new(type, text_tokens.join(' '), args, stack_trace_locations)
597
611
  emit_event(PageEmittedEvents::Console, console_message)
598
612
  end
599
613
 
@@ -1,3 +1,3 @@
1
1
  module Puppeteer
2
- VERSION = '0.40.3'
2
+ VERSION = '0.40.6'
3
3
  end
@@ -30,9 +30,9 @@ Gem::Specification.new do |spec|
30
30
  spec.add_development_dependency 'pry-byebug'
31
31
  spec.add_development_dependency 'rake', '~> 13.0.3'
32
32
  spec.add_development_dependency 'rollbar'
33
- spec.add_development_dependency 'rspec', '~> 3.10.0 '
33
+ spec.add_development_dependency 'rspec', '~> 3.11.0'
34
34
  spec.add_development_dependency 'rspec_junit_formatter' # for CircleCI.
35
- spec.add_development_dependency 'rubocop', '~> 1.24.0'
35
+ spec.add_development_dependency 'rubocop', '~> 1.27.0'
36
36
  spec.add_development_dependency 'rubocop-rspec'
37
37
  spec.add_development_dependency 'sinatra'
38
38
  spec.add_development_dependency 'webrick'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppeteer-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.40.3
4
+ version: 0.40.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - YusukeIwaki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-02-01 00:00:00.000000000 Z
11
+ date: 2022-04-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -142,14 +142,14 @@ dependencies:
142
142
  requirements:
143
143
  - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: 3.10.0
145
+ version: 3.11.0
146
146
  type: :development
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
- version: 3.10.0
152
+ version: 3.11.0
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: rspec_junit_formatter
155
155
  requirement: !ruby/object:Gem::Requirement
@@ -170,14 +170,14 @@ dependencies:
170
170
  requirements:
171
171
  - - "~>"
172
172
  - !ruby/object:Gem::Version
173
- version: 1.24.0
173
+ version: 1.27.0
174
174
  type: :development
175
175
  prerelease: false
176
176
  version_requirements: !ruby/object:Gem::Requirement
177
177
  requirements:
178
178
  - - "~>"
179
179
  - !ruby/object:Gem::Version
180
- version: 1.24.0
180
+ version: 1.27.0
181
181
  - !ruby/object:Gem::Dependency
182
182
  name: rubocop-rspec
183
183
  requirement: !ruby/object:Gem::Requirement
@@ -347,7 +347,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
347
347
  - !ruby/object:Gem::Version
348
348
  version: '0'
349
349
  requirements: []
350
- rubygems_version: 3.3.3
350
+ rubygems_version: 3.3.7
351
351
  signing_key:
352
352
  specification_version: 4
353
353
  summary: A ruby port of puppeteer