shaka-player 4.5.0 → 4.6.0

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.
Files changed (214) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/AUTHORS +2 -0
  3. package/CHANGELOG.md +98 -0
  4. package/CODE_OF_CONDUCT.md +1 -1
  5. package/CONTRIBUTORS +2 -0
  6. package/README.md +38 -32
  7. package/demo/common/asset.js +11 -17
  8. package/demo/common/assets.js +117 -11
  9. package/demo/config.js +28 -1
  10. package/demo/custom.js +52 -38
  11. package/demo/main.js +11 -3
  12. package/demo/search.js +7 -0
  13. package/dist/controls.css +1 -1
  14. package/dist/controls.css.map +1 -1
  15. package/dist/demo.compiled.debug.js +133 -124
  16. package/dist/demo.compiled.debug.map +3 -3
  17. package/dist/demo.compiled.js +133 -124
  18. package/dist/demo.compiled.map +3 -3
  19. package/dist/deps.js +20 -18
  20. package/dist/locales.js +35 -31
  21. package/dist/receiver.compiled.debug.js +58 -50
  22. package/dist/receiver.compiled.debug.map +3 -3
  23. package/dist/receiver.compiled.js +58 -50
  24. package/dist/receiver.compiled.map +3 -3
  25. package/dist/shaka-player.compiled.d.ts +440 -390
  26. package/dist/shaka-player.compiled.debug.d.ts +440 -390
  27. package/dist/shaka-player.compiled.debug.externs.js +162 -77
  28. package/dist/shaka-player.compiled.debug.js +786 -736
  29. package/dist/shaka-player.compiled.debug.map +4 -4
  30. package/dist/shaka-player.compiled.externs.js +162 -77
  31. package/dist/shaka-player.compiled.js +1154 -1121
  32. package/dist/shaka-player.compiled.map +4 -4
  33. package/dist/shaka-player.ui.d.ts +491 -437
  34. package/dist/shaka-player.ui.debug.d.ts +491 -437
  35. package/dist/shaka-player.ui.debug.externs.js +167 -77
  36. package/dist/shaka-player.ui.debug.js +877 -811
  37. package/dist/shaka-player.ui.debug.map +4 -4
  38. package/dist/shaka-player.ui.externs.js +167 -77
  39. package/dist/shaka-player.ui.js +1362 -1318
  40. package/dist/shaka-player.ui.map +4 -4
  41. package/docs/tutorials/ad_monetization.md +54 -0
  42. package/docs/tutorials/basic-usage.md +2 -1
  43. package/docs/tutorials/errors.md +2 -1
  44. package/docs/tutorials/offline.md +10 -8
  45. package/docs/tutorials/plugins.md +5 -0
  46. package/docs/tutorials/ui-customization.md +13 -0
  47. package/docs/tutorials/ui.md +2 -1
  48. package/docs/tutorials/upgrade.md +3 -0
  49. package/externs/managedmediasource.js +24 -0
  50. package/externs/shaka/abr_manager.js +6 -2
  51. package/externs/shaka/cea.js +7 -0
  52. package/externs/shaka/codecs.js +5 -3
  53. package/externs/shaka/manifest.js +21 -5
  54. package/externs/shaka/manifest_parser.js +17 -0
  55. package/externs/shaka/net.js +2 -2
  56. package/externs/shaka/offline.js +9 -2
  57. package/externs/shaka/player.js +53 -7
  58. package/externs/shaka/text.js +3 -1
  59. package/externs/shaka/transmuxer.js +1 -1
  60. package/lib/abr/simple_abr_manager.js +25 -8
  61. package/lib/ads/media_tailor_ad_manager.js +14 -3
  62. package/lib/cast/cast_receiver.js +1 -1
  63. package/lib/cea/cea_decoder.js +28 -0
  64. package/lib/cea/dummy_caption_decoder.js +5 -0
  65. package/lib/dash/dash_parser.js +249 -83
  66. package/lib/dash/mpd_utils.js +52 -31
  67. package/lib/dash/segment_base.js +4 -5
  68. package/lib/dash/segment_list.js +5 -4
  69. package/lib/dash/segment_template.js +83 -26
  70. package/lib/hls/hls_classes.js +28 -43
  71. package/lib/hls/hls_parser.js +761 -649
  72. package/lib/hls/hls_utils.js +64 -5
  73. package/lib/hls/manifest_text_parser.js +7 -11
  74. package/lib/media/adaptation_set.js +20 -13
  75. package/lib/media/adaptation_set_criteria.js +70 -18
  76. package/lib/media/closed_caption_parser.js +13 -0
  77. package/lib/media/content_workarounds.js +6 -0
  78. package/lib/media/drm_engine.js +8 -13
  79. package/lib/media/manifest_parser.js +8 -113
  80. package/lib/media/media_source_capabilities.js +10 -3
  81. package/lib/media/media_source_engine.js +75 -21
  82. package/lib/media/play_rate_controller.js +8 -38
  83. package/lib/media/presentation_timeline.js +7 -1
  84. package/lib/media/quality_observer.js +1 -1
  85. package/lib/media/segment_index.js +5 -5
  86. package/lib/media/segment_prefetch.js +92 -24
  87. package/lib/media/segment_reference.js +67 -1
  88. package/lib/media/segment_utils.js +343 -0
  89. package/lib/media/stall_detector.js +0 -3
  90. package/lib/media/streaming_engine.js +41 -21
  91. package/lib/mss/mss_parser.js +18 -2
  92. package/lib/net/networking_engine.js +66 -12
  93. package/lib/net/networking_utils.js +95 -0
  94. package/lib/offline/indexeddb/v1_storage_cell.js +2 -0
  95. package/lib/offline/indexeddb/v2_storage_cell.js +2 -0
  96. package/lib/offline/manifest_converter.js +2 -0
  97. package/lib/offline/offline_manifest_parser.js +10 -0
  98. package/lib/offline/storage.js +11 -7
  99. package/lib/player.js +993 -1502
  100. package/lib/polyfill/mediasource.js +22 -1
  101. package/lib/text/mp4_ttml_parser.js +2 -2
  102. package/lib/text/srt_text_parser.js +2 -2
  103. package/lib/text/stub_text_displayer.js +51 -0
  104. package/lib/text/text_engine.js +3 -2
  105. package/lib/text/ttml_text_parser.js +28 -5
  106. package/lib/text/ui_text_displayer.js +4 -0
  107. package/lib/text/vtt_text_parser.js +7 -2
  108. package/lib/transmuxer/aac_transmuxer.js +1 -1
  109. package/lib/transmuxer/ac3_transmuxer.js +1 -1
  110. package/lib/transmuxer/ec3_transmuxer.js +1 -1
  111. package/lib/transmuxer/h264.js +5 -4
  112. package/lib/transmuxer/h265.js +5 -4
  113. package/lib/transmuxer/mp3_transmuxer.js +1 -1
  114. package/lib/transmuxer/mpeg_ts_transmuxer.js +1 -1
  115. package/lib/transmuxer/mss_transmuxer.js +1 -1
  116. package/lib/transmuxer/ts_transmuxer.js +33 -13
  117. package/lib/util/buffer_utils.js +26 -4
  118. package/lib/util/content_steering_manager.js +358 -0
  119. package/lib/util/error.js +2 -6
  120. package/lib/util/fairplay_utils.js +1 -1
  121. package/lib/util/fake_event.js +5 -1
  122. package/lib/util/id3_utils.js +50 -1
  123. package/lib/util/manifest_parser_utils.js +12 -8
  124. package/lib/util/mp4_generator.js +2 -4
  125. package/lib/util/mutex.js +59 -0
  126. package/lib/util/object_utils.js +1 -1
  127. package/lib/util/periods.js +154 -179
  128. package/lib/util/platform.js +4 -3
  129. package/lib/util/player_configuration.js +35 -6
  130. package/lib/util/stream_utils.js +105 -98
  131. package/lib/util/ts_parser.js +124 -73
  132. package/lib/util/uint8array_utils.js +5 -1
  133. package/lib/util/xml_utils.js +5 -0
  134. package/maintained-branches.md +2 -2
  135. package/package.json +1 -1
  136. package/roadmap.md +15 -5
  137. package/shaka-player.uncompiled.js +0 -1
  138. package/test/abr/simple_abr_manager_unit.js +33 -33
  139. package/test/ads/ad_manager_unit.js +3 -2
  140. package/test/cast/cast_receiver_integration.js +3 -4
  141. package/test/cast/cast_utils_unit.js +2 -0
  142. package/test/cea/cea_decoder_unit.js +7 -0
  143. package/test/codec_switching/codec_switching_integration.js +2 -1
  144. package/test/dash/dash_parser_integration.js +2 -1
  145. package/test/dash/dash_parser_live_unit.js +0 -19
  146. package/test/dash/dash_parser_manifest_unit.js +494 -2
  147. package/test/dash/mpd_utils_unit.js +61 -30
  148. package/test/hls/hls_live_unit.js +12 -3
  149. package/test/hls/hls_parser_unit.js +377 -27
  150. package/test/hls/manifest_text_parser_unit.js +32 -99
  151. package/test/media/adaptation_set_criteria_unit.js +172 -21
  152. package/test/media/adaptation_set_unit.js +1 -0
  153. package/test/media/content_workarounds_unit.js +1 -1
  154. package/test/media/drm_engine_unit.js +3 -3
  155. package/test/media/media_source_engine_unit.js +1 -1
  156. package/test/media/play_rate_controller_unit.js +0 -48
  157. package/test/media/segment_index_unit.js +2 -1
  158. package/test/mss/mss_player_integration.js +3 -2
  159. package/test/net/networking_engine_unit.js +3 -3
  160. package/test/{media/manifest_parser_unit.js → net/networking_utils_unit.js} +2 -2
  161. package/test/offline/manifest_convert_unit.js +9 -0
  162. package/test/offline/offline_integration.js +2 -1
  163. package/test/offline/storage_integration.js +21 -9
  164. package/test/player_external.js +4 -2
  165. package/test/player_integration.js +15 -4
  166. package/test/player_load_graph_integration.js +47 -151
  167. package/test/player_src_equals_external.js +1 -0
  168. package/test/player_src_equals_integration.js +71 -4
  169. package/test/player_unit.js +80 -27
  170. package/test/test/util/fake_media_source_engine.js +5 -0
  171. package/test/test/util/fake_networking_engine.js +9 -1
  172. package/test/test/util/fake_segment_prefetch.js +3 -0
  173. package/test/test/util/manifest_generator.js +4 -0
  174. package/test/test/util/manifest_parser_util.js +14 -2
  175. package/test/test/util/offline_utils.js +1 -0
  176. package/test/test/util/simple_fakes.js +2 -2
  177. package/test/test/util/streaming_engine_util.js +3 -0
  178. package/test/test/util/test_scheme.js +6 -0
  179. package/test/test/util/ui_utils.js +4 -3
  180. package/test/text/mp4_ttml_parser_unit.js +4 -4
  181. package/test/text/srt_text_parser_unit.js +1 -1
  182. package/test/text/text_displayer_layout_unit.js +1 -1
  183. package/test/text/text_engine_unit.js +6 -0
  184. package/test/text/ttml_text_parser_unit.js +33 -2
  185. package/test/transmuxer/transmuxer_engine_integration.js +9 -0
  186. package/test/transmuxer/transmuxer_integration.js +33 -3
  187. package/test/ui/ad_ui_unit.js +2 -2
  188. package/test/ui/ui_customization_unit.js +17 -16
  189. package/test/ui/ui_integration.js +30 -20
  190. package/test/ui/ui_unit.js +25 -19
  191. package/test/util/buffer_utils_unit.js +1 -1
  192. package/test/util/content_steering_manager_unit.js +427 -0
  193. package/test/util/id3_utils_unit.js +52 -0
  194. package/test/util/stream_utils_unit.js +17 -113
  195. package/test/util/ts_parser_unit.js +3 -3
  196. package/third_party/closure-uri/uri.js +13 -1
  197. package/ui/audio_language_selection.js +9 -2
  198. package/ui/externs/ui.js +17 -2
  199. package/ui/language_utils.js +43 -8
  200. package/ui/less/range_elements.less +33 -0
  201. package/ui/locales/en.json +1 -0
  202. package/ui/locales/es-419.json +1 -0
  203. package/ui/locales/es.json +1 -0
  204. package/ui/locales/source.json +4 -0
  205. package/ui/localization.js +10 -0
  206. package/ui/remote_button.js +3 -1
  207. package/ui/resolution_selection.js +47 -10
  208. package/ui/seek_bar.js +296 -3
  209. package/ui/text_selection.js +2 -1
  210. package/ui/ui.js +6 -2
  211. package/lib/routing/node.js +0 -23
  212. package/lib/routing/payload.js +0 -47
  213. package/lib/routing/walker.js +0 -587
  214. package/test/routing/walker_unit.js +0 -505
@@ -1,505 +0,0 @@
1
- /*! @license
2
- * Shaka Player
3
- * Copyright 2016 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
-
7
- describe('Walker', () => {
8
- const AbortableOperation = shaka.util.AbortableOperation;
9
-
10
- // For our tests, we will not use the payload in our routing logic. To
11
- // avoid distracting the reader with payload details, hide them here.
12
- const payload = {
13
- factory: null,
14
- mediaElement: null,
15
- mimeType: null,
16
- startTime: null,
17
- startTimeOfLoad: NaN,
18
- uri: null,
19
- keepAdManager: false,
20
- };
21
-
22
- // The graph topology that we will be using for our tests.
23
- //
24
- // [ A ] ---> [ B ] ---> [ E ]
25
- // ^ |
26
- // | v
27
- // [ D ] <--- [ C ]
28
- //
29
- /** @type {shaka.routing.Node} */
30
- const nodeA = {name: 'a'};
31
- /** @type {shaka.routing.Node} */
32
- const nodeB = {name: 'b'};
33
- /** @type {shaka.routing.Node} */
34
- const nodeC = {name: 'c'};
35
- /** @type {shaka.routing.Node} */
36
- const nodeD = {name: 'd'};
37
- /** @type {shaka.routing.Node} */
38
- const nodeE = {name: 'e'};
39
-
40
- /**
41
- * @param {shaka.routing.Node} at
42
- * @param {shaka.routing.Node} goingTo
43
- * @return {shaka.routing.Node}
44
- */
45
- function getNext(at, goingTo) {
46
- // In this graph, where you start determines where you go. The one exception
47
- // is node B, which acts as a fork between nodes C and E. This fork is
48
- // important in testing interrupts.
49
-
50
- let goTo = null;
51
-
52
- if (at == nodeA) {
53
- goTo = nodeB;
54
- }
55
-
56
- if (at == nodeB) {
57
- goTo = goingTo == nodeE ? nodeE : nodeC;
58
- }
59
-
60
- if (at == nodeC) {
61
- goTo = nodeD;
62
- }
63
-
64
- if (at == nodeD) {
65
- goTo = nodeA;
66
- }
67
-
68
- goog.asserts.assert(goTo, 'We should have found a next step.');
69
- return goTo;
70
- }
71
-
72
- /** @type {!shaka.routing.Walker} */
73
- let walker;
74
-
75
- /** @type {!jasmine.Spy} */
76
- let enterNodeSpy;
77
-
78
- /** @type {!jasmine.Spy} */
79
- let handleErrorSpy;
80
-
81
- /** @type {!jasmine.Spy} */
82
- let idleSpy;
83
-
84
- beforeEach(() => {
85
- enterNodeSpy = jasmine.createSpy('enterNode');
86
- enterNodeSpy.and.returnValue(AbortableOperation.completed(undefined));
87
-
88
- handleErrorSpy = jasmine.createSpy('handleError');
89
-
90
- idleSpy = jasmine.createSpy('idle');
91
-
92
- const implementation = {
93
- getNext: (at, has, goingTo, wants) => getNext(at, goingTo),
94
- enterNode: shaka.test.Util.spyFunc(enterNodeSpy),
95
- handleError: shaka.test.Util.spyFunc(handleErrorSpy),
96
- onIdle: shaka.test.Util.spyFunc(idleSpy),
97
- };
98
-
99
- walker = new shaka.routing.Walker(nodeA, payload, implementation);
100
- });
101
-
102
- afterEach(async () => {
103
- await walker.destroy();
104
- });
105
-
106
- it('enters idle after initialization', async () => {
107
- await waitOnSpy(idleSpy);
108
- });
109
-
110
- it('enters idle after completing route', async () => {
111
- // Execute a route but then wait a couple interrupter cycles to allow the
112
- // walker time to idle.
113
- await completesRoute(startNewRoute(nodeD, /* interruptible= */ false));
114
- await waitOnSpy(idleSpy);
115
- });
116
-
117
- it('enters idle after error', async () => {
118
- // The specific error does not matter.
119
- const error = new shaka.util.Error(
120
- shaka.util.Error.Severity.CRITICAL,
121
- shaka.util.Error.Category.TEXT,
122
- shaka.util.Error.Code.BAD_ENCODING);
123
-
124
- enterNodeSpy.and.callFake((at) => {
125
- return at == nodeC ?
126
- shaka.util.AbortableOperation.failed(error) :
127
- shaka.util.AbortableOperation.completed(undefined);
128
- });
129
-
130
- handleErrorSpy.and.returnValue(nodeA);
131
-
132
- // Go to nodeD, passing through nodeC. This will fail, calling handleError,
133
- // and returning the walker to nodeA. Wait a couple interrupter cycles to
134
- // allow the walker time to idle. The route needs to be interruptible since
135
- // we are going to return an aborted operation.
136
- await failsRoute(startNewRoute(nodeD, /* interruptible= */ true));
137
- await waitOnSpy(idleSpy);
138
- });
139
-
140
- // The walker should move node-by-node, so starting in node A (see beforeEach)
141
- // and going to nodeD, we should see the walker enter nodeB, nodeC, and nodeD.
142
- // We won't see it enter nodeA because it starts there and therefore never
143
- // "enters" that node.
144
- it('moves node-by-node', async () => {
145
- // We don't expect any errors in this test.
146
- handleErrorSpy.and.callFake(fail);
147
-
148
- await completesRoute(startNewRoute(nodeD, /* interruptible= */ false));
149
-
150
- const steps = getStepsTaken();
151
- expect(steps).toEqual([nodeB, nodeC, nodeD]);
152
- });
153
-
154
- // We want to make sure that if a route is registered as interruptible, we
155
- // can interrupt the route and start a new route.
156
- //
157
- // For this we will have the walker start going to node E, but when it
158
- // enters node B (the fork) we will interrupt it and tell it to go to
159
- // node A.
160
- it('can interrupt interruptible routes', async () => {
161
- // We don't expect any errors in this test.
162
- handleErrorSpy.and.callFake(fail);
163
-
164
- /** @type {!shaka.util.PublicPromise} */
165
- const atA = new shaka.util.PublicPromise();
166
- const interrupt = () => {
167
- const goToA = startNewRoute(nodeA, /* interruptible= */ false);
168
- goToA.onEnd = () => atA.resolve();
169
- };
170
-
171
- const goingToE = startNewRoute(nodeE, /* interruptible= */ true);
172
- goingToE.onEnter = (node) => {
173
- if (node == nodeB) {
174
- interrupt();
175
- }
176
- };
177
-
178
- await atA;
179
-
180
- const steps = getStepsTaken();
181
- expect(steps).toEqual([nodeB, nodeC, nodeD, nodeA]);
182
- });
183
-
184
- // We want to make sure that a non-interruptible route cannot be interrupted
185
- // by starting a new route. To do this, we are going to start off by going to
186
- // node c. When we get to node b, we will try to start a new route to node e.
187
- //
188
- // If the route was interrupted, we would go to straight to node e (node b is
189
- // a fork in the graph). However, since we expect the first route to finish,
190
- // we expect to see the walker go to node c and then continue around to get to
191
- // node e.
192
- it('cannot interrupt non-interruptible routes', async () => {
193
- // We don't expect any errors in this test.
194
- handleErrorSpy.and.callFake(fail);
195
-
196
- /** @type {!shaka.util.PublicPromise} */
197
- const atE = new shaka.util.PublicPromise();
198
-
199
- const interrupt = () => {
200
- const goToE = startNewRoute(nodeE, /* interruptible= */ false);
201
- goToE.onEnd = () => atE.resolve();
202
- };
203
-
204
- // Create a "trap" so that once we enter node B (the fork) that we will
205
- // issue a new route - this will ensure that we are trying to interrupt a
206
- // route mid-execution.
207
- const goingToC = startNewRoute(nodeC, /* interruptible= */ false);
208
- goingToC.onEnter = (node) => {
209
- interrupt();
210
- };
211
-
212
- await atE;
213
-
214
- const steps = getStepsTaken();
215
- expect(steps).toEqual([
216
- // First route.
217
- nodeB, nodeC,
218
- // Second route.
219
- nodeD, nodeA, nodeB, nodeE,
220
- ]);
221
- });
222
-
223
- // We do not want to execute steps for a route that will be interrupted
224
- // right after it starts. For this example, we queue-up three routes:
225
- // 1. Non-interruptible
226
- // 2. Interruptible
227
- // 3. Interruptible
228
- //
229
- // What we expect to see is that Route 1 finishes, Route 2 starts but takes
230
- // no steps, and Route 3 finishes.
231
- it('does not take steps for route interrupted before starting', async () => {
232
- /**
233
- * When a route starts, it will assign this value to its id (1, 2, or 3)
234
- * so we always know who was the most recent route to start.
235
- *
236
- * @type {?number}
237
- */
238
- let currentRoute = null;
239
-
240
- /**
241
- * We use this set to know who took steps. When the walker takes a step
242
- * we will add |currentRoute| to this set. That way we will know what
243
- * route we took steps on.
244
- *
245
- * @type {!Array.<?number>}
246
- */
247
- const tookSteps = [];
248
-
249
- enterNodeSpy.and.callFake((node) => {
250
- tookSteps.push(currentRoute);
251
- return AbortableOperation.completed(undefined);
252
- });
253
-
254
- const route1 = startNewRoute(nodeD, /* interruptible= */ false);
255
- route1.onStart = () => {
256
- currentRoute = 1;
257
- };
258
-
259
- const route2 = startNewRoute(nodeC, /* interruptible= */ true);
260
- route2.onStart = () => {
261
- currentRoute = 2;
262
- };
263
-
264
- const route3 = startNewRoute(nodeE, /* interruptible= */ true);
265
- route3.onStart = () => {
266
- currentRoute = 3;
267
- };
268
-
269
- // Wait until we get to the end of route 3, that should be the end.
270
- await completesRoute(route3);
271
-
272
- // Make sure we had the correct routes when taking each step.
273
- expect(tookSteps).toEqual([
274
- 1, // A to B
275
- 1, // B to C
276
- 1, // C to D
277
- 3, // D to A
278
- 3, // A to B
279
- 3, // B to E
280
- ]);
281
- });
282
-
283
- // When we destroy the walker, it should cancel all routes - even the
284
- // non-interruptible routes.
285
- it('cancels all routes when destroyed', async () => {
286
- // Start-up a couple routes, and then destroy the walker. We expect to see
287
- // both routes have their |onCancel| callbacks called. We make the first
288
- // be non-interruptible and the second route interruptible so that we can
289
- // see both types be cancelled by |destroy|. The non-interruptible route
290
- // must before first or else it would interrupt the other route.
291
- const goToC = startNewRoute(nodeC, /* interruptible= */ false);
292
- const goToB = startNewRoute(nodeB, /* interruptible= */ true);
293
-
294
- /** @type {!jasmine.Spy} */
295
- const canceledCSpy = jasmine.createSpy('cancel c');
296
- goToC.onCancel = shaka.test.Util.spyFunc(canceledCSpy);
297
-
298
- /** @type {!jasmine.Spy} */
299
- const canceledBSpy = jasmine.createSpy('cancel b');
300
- goToB.onCancel = shaka.test.Util.spyFunc(canceledBSpy);
301
-
302
- await walker.destroy();
303
-
304
- expect(canceledCSpy).toHaveBeenCalled();
305
- expect(canceledBSpy).toHaveBeenCalled();
306
- });
307
-
308
- it('calls handleError when step fails', async () => {
309
- // The specific error does not matter.
310
- const error = new shaka.util.Error(
311
- shaka.util.Error.Severity.CRITICAL,
312
- shaka.util.Error.Category.TEXT,
313
- shaka.util.Error.Code.BAD_ENCODING);
314
-
315
- // Make the walker fail when it hits node C. This should allow us to
316
- // exercise the |handleError| path.
317
- enterNodeSpy.and.callFake((at) => {
318
- return at == nodeC ?
319
- shaka.util.AbortableOperation.failed(error) :
320
- shaka.util.AbortableOperation.completed(undefined);
321
- });
322
-
323
- // We want to handle the error and return to a safe state, so just put the
324
- // walker back at node A.
325
- handleErrorSpy.and.returnValue(nodeA);
326
-
327
- // Go to D (passing through C). This should throw an error, so wait for the
328
- // error to be seen. The route must be abortable because we are going to
329
- // throw an abortable error.
330
- await failsRoute(startNewRoute(nodeD, /* interruptible= */ true));
331
-
332
- expect(handleErrorSpy).toHaveBeenCalled();
333
- });
334
-
335
- // When we interrupt a route that has a step that can be aborted, it should
336
- // abort the operation and enter the error recovery mode.
337
- //
338
- // To model this we will make a node be a never-resolving node. We will enter
339
- // the node and then get stuck. From there we will request a new route, the
340
- // blocked op will abort, and then we will go to our new destination.
341
- it('can abort current step', async () => {
342
- // Because we need a node to start at after resetting, we will just use A.
343
- // There is no special reason for node A.
344
- handleErrorSpy.and.returnValue(Promise.resolve(nodeA));
345
-
346
- // Block when we enter node C so that we can re-route to node E and finish a
347
- // path.
348
- blockWalkerAt(nodeC);
349
-
350
- // Wait for us to enter node d before continuing. We introduce a small delay
351
- // to ensure that we are "stuck" on the abortable operation.
352
- const goingToD = startNewRoute(nodeD, /* interruptible= */ true);
353
- await waitUntilEntering(goingToD, nodeC);
354
- await shaka.test.Util.shortDelay();
355
-
356
- await completesRoute(startNewRoute(nodeE, /* interruptible= */ true));
357
- expect(handleErrorSpy).toHaveBeenCalled();
358
- });
359
-
360
- // If we are in the middle of a node and |destroy| is called, we want to
361
- // ensure that we exit as soon as possible. If the current step is abortable
362
- // then we want to abort.
363
- it('can abort current step with destroy', async () => {
364
- // Because we need a node to start at after resetting, we will just use A.
365
- // There is no special reason for node A.
366
- handleErrorSpy.and.returnValue(Promise.resolve(nodeA));
367
-
368
- // Block when we enter node C so that we can re-route to node E and finish a
369
- // path.
370
- blockWalkerAt(nodeC);
371
-
372
- // Wait for us to enter node d before continuing. We introduce a small delay
373
- // to ensure that we are "stuck" on the abortable operation.
374
- const goingToD = startNewRoute(nodeD, /* interruptible= */ true);
375
- await waitUntilEntering(goingToD, nodeC);
376
- await shaka.test.Util.shortDelay();
377
-
378
- // We are "stuck" in nodeC. We will now destroy the walker which should
379
- // abort the nodeC step, enter error recovery mode, and then shutdown.
380
- await walker.destroy();
381
- expect(handleErrorSpy).toHaveBeenCalled();
382
- });
383
-
384
- /**
385
- * Ask the walker to start a new route. Since the requests from our tests
386
- * are very basic, wrapping the call should not hide too much information.
387
- *
388
- * @param {shaka.routing.Node} goingTo
389
- * @param {boolean} interruptible
390
- *
391
- * @return {shaka.routing.Walker.Listeners}
392
- */
393
- function startNewRoute(goingTo, interruptible) {
394
- return walker.startNewRoute((currentPayload) => {
395
- return {
396
- node: goingTo,
397
- payload: payload,
398
- interruptible: interruptible,
399
- };
400
- });
401
- }
402
-
403
- /**
404
- * Get the series of nodes that the walker went through during its "journey".
405
- *
406
- * @return {!Array.<shaka.routing.Node>}
407
- */
408
- function getStepsTaken() {
409
- // Use |onNode| to get the steps that completed.
410
- return enterNodeSpy.calls.allArgs().map((args) => args[0]);
411
- }
412
-
413
- /**
414
- * Configure the |enterNodeSpy| so that we will block when we enter |node|. In
415
- * order to unblock, the route will need to be interrupted.
416
- *
417
- * @param {shaka.routing.Node} node
418
- */
419
- function blockWalkerAt(node) {
420
- /** @type {!shaka.util.AbortableOperation} */
421
- const completedOp = AbortableOperation.completed(undefined);
422
-
423
- /** @type {!shaka.util.PublicPromise} */
424
- const waitForever = new shaka.util.PublicPromise();
425
-
426
- /** @type {!shaka.util.AbortableOperation} */
427
- const blockingOp = new AbortableOperation(
428
- waitForever,
429
- () => {
430
- waitForever.reject(new shaka.util.Error(
431
- shaka.util.Error.Severity.CRITICAL,
432
- shaka.util.Error.Category.PLAYER,
433
- shaka.util.Error.Code.OPERATION_ABORTED));
434
-
435
- return waitForever;
436
- });
437
-
438
- enterNodeSpy.and.callFake((at) => {
439
- return at == node ? blockingOp : completedOp;
440
- });
441
- }
442
-
443
- /**
444
- * Create a promise that will resolve when we enter |node| while on the route
445
- * that produced |events|.
446
- *
447
- * @param {shaka.routing.Walker.Listeners} events
448
- * @param {shaka.routing.Node} node
449
- * @return {!Promise}
450
- */
451
- function waitUntilEntering(events, node) {
452
- return new Promise((resolve) => {
453
- events.onEnter = (node) => {
454
- if (node == nodeC) {
455
- resolve();
456
- }
457
- };
458
- });
459
- }
460
-
461
- /**
462
- * Create a promise from a walker's route's listeners. This assumes that the
463
- * route should finish. The promise will resolve if the route completes and
464
- * will reject if the route fails to complete for any reason.
465
- *
466
- * @param {shaka.routing.Walker.Listeners} events
467
- * @return {!Promise}
468
- */
469
- function completesRoute(events) {
470
- return new Promise((resolve, reject) => {
471
- events.onEnd = resolve;
472
- events.onCancel = reject;
473
- events.onError = reject;
474
- });
475
- }
476
-
477
- /**
478
- * Create a promise from a walker's route's listeners. This assumes that the
479
- * route should not finish. The promise will resolve if the route fails and
480
- * will reject if the route completes.
481
- *
482
- * @param {shaka.routing.Walker.Listeners} events
483
- * @return {!Promise}
484
- */
485
- function failsRoute(events) {
486
- return new Promise((resolve, reject) => {
487
- events.onEnd = reject;
488
- events.onCancel = resolve;
489
- events.onError = resolve;
490
- });
491
- }
492
-
493
- /**
494
- * Wrap a spy in a promise so that the promise will resolve once the spy is
495
- * called.
496
- *
497
- * @param {!jasmine.Spy} spy
498
- * @return {!Promise}
499
- */
500
- function waitOnSpy(spy) {
501
- return new Promise((resolve) => {
502
- spy.and.callFake(resolve);
503
- });
504
- }
505
- });