puppeteer-ruby 0.40.3 → 0.40.6

Sign up to get free protection for your applications and to get access to all the features.
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