Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // downloads api test | 5 // downloads api test |
| 6 // browser_tests.exe --gtest_filter=DownloadsApiTest.Downloads | 6 // browser_tests.exe --gtest_filter=DownloadsApiTest.Downloads |
| 7 | 7 |
| 8 // Uncomment this when the apitest is re-enabled. | |
| 9 // console.debug = function() {}; | |
|
cbentzel
2012/03/01 22:16:26
I'd prefer it if you introduced a new function whi
benjhayden
2012/03/02 14:43:47
I tried putting console.log inside another functio
| |
| 10 | |
| 11 function debugObject(obj) { | |
| 12 for (var property in obj) { | |
| 13 console.debug(property + ': ' + obj[property]); | |
| 14 } | |
| 15 } | |
| 16 | |
| 8 var downloads = chrome.experimental.downloads; | 17 var downloads = chrome.experimental.downloads; |
| 9 | 18 |
| 10 chrome.test.getConfig(function(testConfig) { | 19 chrome.test.getConfig(function(testConfig) { |
| 11 function getURL(path) { | 20 function getURL(path) { |
| 12 return 'http://localhost:' + testConfig.testServer.port + '/' + path; | 21 return 'http://localhost:' + testConfig.testServer.port + '/' + path; |
| 13 } | 22 } |
| 14 | 23 |
| 15 var nextId = 0; | 24 var nextId = 0; |
| 16 function getNextId() { | 25 function getNextId() { |
| 17 return nextId++; | 26 return nextId++; |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 48 | 57 |
| 49 // This URL should only work with the POST method and a request body | 58 // This URL should only work with the POST method and a request body |
| 50 // containing 'BODY'. | 59 // containing 'BODY'. |
| 51 var POST_URL = getURL('files/post/downloads/a_zip_file.zip?' + | 60 var POST_URL = getURL('files/post/downloads/a_zip_file.zip?' + |
| 52 'expected_body=BODY'); | 61 'expected_body=BODY'); |
| 53 | 62 |
| 54 // This URL should only work with headers 'Foo: bar' and 'Qx: yo'. | 63 // This URL should only work with headers 'Foo: bar' and 'Qx: yo'. |
| 55 var HEADERS_URL = getURL('files/downloads/a_zip_file.zip?' + | 64 var HEADERS_URL = getURL('files/downloads/a_zip_file.zip?' + |
| 56 'expected_headers=Foo:bar&expected_headers=Qx:yo'); | 65 'expected_headers=Foo:bar&expected_headers=Qx:yo'); |
| 57 | 66 |
| 67 // A simple handler that requires http auth basic. | |
| 68 var AUTH_BASIC_URL = getURL('auth-basic'); | |
| 69 | |
| 70 // This is just base64 of 'username:secret'. | |
| 71 var AUTHORIZATION = 'dXNlcm5hbWU6c2VjcmV0'; | |
| 72 | |
| 58 chrome.test.runTests([ | 73 chrome.test.runTests([ |
| 59 // TODO(benjhayden): Test onErased using remove(). | 74 // TODO(benjhayden): Test onErased using remove(). |
| 60 | 75 |
| 61 // TODO(benjhayden): Sub-directories depend on http://crbug.com/109443 | 76 // TODO(benjhayden): Sub-directories depend on http://crbug.com/109443 |
| 62 // TODO(benjhayden): Windows slashes. | 77 // TODO(benjhayden): Windows slashes. |
| 63 // function downloadSubDirectoryFilename() { | 78 // function downloadSubDirectoryFilename() { |
| 64 // var downloadId = getNextId(); | 79 // var downloadId = getNextId(); |
| 65 // var callbackCompleted = chrome.test.callbackAdded(); | 80 // var callbackCompleted = chrome.test.callbackAdded(); |
| 66 // function myListener(delta) { | 81 // function myListener(delta) { |
| 67 // if ((delta.id != downloadId) || | 82 // if ((delta.id != downloadId) || |
| 68 // !delta.filename || | 83 // !delta.filename || |
| 69 // (delta.filename.new.indexOf('/foo/slow') == -1)) | 84 // (delta.filename.new.indexOf('/foo/slow') == -1)) |
| 70 // return; | 85 // return; |
| 71 // downloads.onChanged.removeListener(myListener); | 86 // downloads.onChanged.removeListener(myListener); |
| 72 // callbackCompleted(); | 87 // callbackCompleted(); |
| 73 // } | 88 // } |
| 74 // downloads.onChanged.addListener(myListener); | 89 // downloads.onChanged.addListener(myListener); |
| 75 // downloads.download( | 90 // downloads.download( |
| 76 // {'url': SAFE_FAST_URL, 'filename': 'foo/slow'}, | 91 // {'url': SAFE_FAST_URL, 'filename': 'foo/slow'}, |
| 77 // chrome.test.callback(function(id) { | 92 // chrome.test.callback(function(id) { |
| 78 // chrome.test.assertEq(downloadId, id); | 93 // chrome.test.assertEq(downloadId, id); |
| 79 // })); | 94 // })); |
| 80 // }, | 95 // }, |
| 81 | 96 |
| 82 function downloadSimple() { | 97 function downloadSimple() { |
| 83 // Test that we can begin a download. | 98 // Test that we can begin a download. |
| 84 var downloadId = getNextId(); | 99 var downloadId = getNextId(); |
| 85 console.log(downloadId); | 100 console.debug(downloadId); |
| 86 downloads.download( | 101 downloads.download( |
| 87 {'url': SAFE_FAST_URL}, | 102 {'url': SAFE_FAST_URL}, |
| 88 chrome.test.callback(function(id) { | 103 chrome.test.callback(function(id) { |
| 89 chrome.test.assertEq(downloadId, id); | 104 chrome.test.assertEq(downloadId, id); |
| 90 })); | 105 })); |
| 91 }, | 106 }, |
| 92 | 107 |
| 108 function downloadOnChanged() { | |
| 109 // Test that download completion is detectable by an onChanged event | |
| 110 // listener. | |
| 111 var downloadId = getNextId(); | |
| 112 console.debug(downloadId); | |
| 113 var callbackCompleted = chrome.test.callbackAdded(); | |
| 114 function myListener(delta) { | |
| 115 console.debug(delta.id); | |
| 116 if ((delta.id != downloadId) || | |
| 117 !delta.state) | |
| 118 return; | |
| 119 chrome.test.assertEq(downloads.STATE_COMPLETE, delta.state.new); | |
| 120 console.debug(downloadId); | |
| 121 downloads.onChanged.removeListener(myListener); | |
| 122 callbackCompleted(); | |
| 123 } | |
| 124 downloads.onChanged.addListener(myListener); | |
| 125 downloads.download( | |
| 126 {"url": SAFE_FAST_URL}, | |
| 127 chrome.test.callback(function(id) { | |
| 128 console.debug(downloadId); | |
| 129 chrome.test.assertEq(downloadId, id); | |
| 130 })); | |
| 131 }, | |
| 132 | |
| 133 function downloadAuthBasicFail() { | |
| 134 var downloadId = getNextId(); | |
| 135 console.debug(downloadId); | |
| 136 | |
| 137 var changedCompleted = chrome.test.callbackAdded(); | |
| 138 function changedListener(delta) { | |
| 139 console.debug(delta.id); | |
| 140 // Ignore onChanged events for downloads besides our own, or events that | |
| 141 // signal any change besides completion. | |
| 142 if ((delta.id != downloadId) || | |
| 143 !delta.state || | |
| 144 !delta.error) | |
| 145 return; | |
| 146 console.debug(downloadId); | |
| 147 chrome.test.assertEq(downloads.STATE_INTERRUPTED, delta.state.new); | |
| 148 chrome.test.assertEq(30, delta.error.new); | |
| 149 downloads.onChanged.removeListener(changedListener); | |
| 150 if (changedCompleted) { | |
| 151 changedCompleted(); | |
| 152 changedCompleted = null; | |
| 153 } | |
| 154 } | |
| 155 downloads.onChanged.addListener(changedListener); | |
| 156 | |
| 157 // Sometimes the DownloadsEventRouter detects the item for the first time | |
| 158 // after the item has already been interrupted. In this case, the | |
| 159 // onChanged event never fires, so run the changedListener manually. If | |
| 160 // the DownloadsEventRouter detects the item before it's interrupted, then | |
| 161 // the onChanged event should fire correctly. | |
| 162 var createdCompleted = chrome.test.callbackAdded(); | |
| 163 function createdListener(createdItem) { | |
| 164 console.debug(createdItem.id); | |
| 165 // Ignore events for any download besides our own. | |
| 166 if (createdItem.id != downloadId) | |
| 167 return; | |
| 168 console.debug(downloadId); | |
| 169 downloads.onCreated.removeListener(createdListener); | |
| 170 createdCompleted(); | |
| 171 if (createdItem.state == downloads.STATE_INTERRUPTED) { | |
| 172 changedListener({id: downloadId, state: {new: createdItem.state}, | |
| 173 error: {new: createdItem.error}}); | |
| 174 } | |
| 175 } | |
| 176 downloads.onCreated.addListener(createdListener); | |
| 177 | |
| 178 downloads.download( | |
| 179 {'url': AUTH_BASIC_URL, | |
| 180 'filename': downloadId + '.txt'}, | |
| 181 chrome.test.callback(function(id) { | |
| 182 console.debug(downloadId); | |
| 183 chrome.test.assertEq(downloadId, id); | |
| 184 })); | |
| 185 }, | |
| 186 | |
| 187 function downloadAuthBasicSucceed() { | |
| 188 var downloadId = getNextId(); | |
| 189 console.debug(downloadId); | |
| 190 | |
| 191 var changedCompleted = chrome.test.callbackAdded(); | |
| 192 function changedListener(delta) { | |
| 193 console.debug(delta.id); | |
| 194 // Ignore onChanged events for downloads besides our own, or events that | |
| 195 // signal any change besides completion. | |
| 196 if ((delta.id != downloadId) || | |
| 197 !delta.state) | |
| 198 return; | |
| 199 chrome.test.assertEq(downloads.STATE_COMPLETE, delta.state.new); | |
| 200 console.debug(downloadId); | |
| 201 downloads.onChanged.removeListener(changedListener); | |
| 202 changedCompleted(); | |
| 203 } | |
| 204 downloads.onChanged.addListener(changedListener); | |
| 205 | |
| 206 downloads.download( | |
| 207 {'url': AUTH_BASIC_URL, | |
| 208 'headers': [{'name': 'Authorization', | |
| 209 'value': 'Basic ' + AUTHORIZATION}], | |
| 210 'filename': downloadId + '.txt'}, | |
| 211 chrome.test.callback(function(id) { | |
| 212 console.debug(downloadId); | |
| 213 chrome.test.assertEq(downloadId, id); | |
| 214 })); | |
| 215 }, | |
| 216 | |
| 93 function downloadPostSuccess() { | 217 function downloadPostSuccess() { |
| 94 // Test the |method| download option. | 218 // Test the |method| download option. |
| 95 var downloadId = getNextId(); | 219 var downloadId = getNextId(); |
| 96 console.log(downloadId); | 220 console.debug(downloadId); |
| 97 var changedCompleted = chrome.test.callbackAdded(); | 221 var changedCompleted = chrome.test.callbackAdded(); |
| 98 function changedListener(delta) { | 222 function changedListener(delta) { |
| 99 console.log(delta.id); | 223 console.debug(delta.id); |
| 100 // Ignore onChanged events for downloads besides our own, or events that | 224 // Ignore onChanged events for downloads besides our own, or events that |
| 101 // signal any change besides completion. | 225 // signal any change besides completion. |
| 102 if ((delta.id != downloadId) || | 226 if ((delta.id != downloadId) || |
| 103 !delta.state || | 227 !delta.state) |
| 104 (delta.state.new != downloads.STATE_COMPLETE)) | |
| 105 return; | 228 return; |
| 106 console.log(downloadId); | 229 chrome.test.assertEq(downloads.STATE_COMPLETE, delta.state.new); |
| 230 console.debug(downloadId); | |
| 107 downloads.search({id: downloadId}, | 231 downloads.search({id: downloadId}, |
| 108 chrome.test.callback(function(items) { | 232 chrome.test.callback(function(items) { |
| 109 console.log(downloadId); | 233 console.debug(downloadId); |
| 110 chrome.test.assertEq(1, items.length); | 234 chrome.test.assertEq(1, items.length); |
| 111 chrome.test.assertEq(downloadId, items[0].id); | 235 chrome.test.assertEq(downloadId, items[0].id); |
| 236 debugObject(items[0]); | |
| 112 var EXPECTED_SIZE = 164; | 237 var EXPECTED_SIZE = 164; |
| 113 chrome.test.assertEq(EXPECTED_SIZE, items[0].totalBytes); | |
| 114 chrome.test.assertEq(EXPECTED_SIZE, items[0].fileSize); | |
| 115 chrome.test.assertEq(EXPECTED_SIZE, items[0].bytesReceived); | 238 chrome.test.assertEq(EXPECTED_SIZE, items[0].bytesReceived); |
| 116 })); | 239 })); |
| 117 downloads.onChanged.removeListener(changedListener); | 240 downloads.onChanged.removeListener(changedListener); |
| 118 changedCompleted(); | 241 changedCompleted(); |
| 119 } | 242 } |
| 120 downloads.onChanged.addListener(changedListener); | 243 downloads.onChanged.addListener(changedListener); |
| 121 | 244 |
| 122 downloads.download( | 245 downloads.download( |
| 123 {'url': POST_URL, | 246 {'url': POST_URL, |
| 124 'method': 'POST', | 247 'method': 'POST', |
| 125 'filename': downloadId + '.txt', | 248 'filename': downloadId + '.txt', |
| 126 'body': 'BODY'}, | 249 'body': 'BODY'}, |
| 127 chrome.test.callback(function(id) { | 250 chrome.test.callback(function(id) { |
| 128 console.log(downloadId); | 251 console.debug(downloadId); |
| 129 chrome.test.assertEq(downloadId, id); | 252 chrome.test.assertEq(downloadId, id); |
| 130 })); | 253 })); |
| 131 }, | 254 }, |
| 132 | 255 |
| 133 function downloadPostWouldFailWithoutMethod() { | 256 function downloadPostWouldFailWithoutMethod() { |
| 134 // Test that downloadPostSuccess would fail if the resource requires the | 257 // Test that downloadPostSuccess would fail if the resource requires the |
| 135 // POST method, and chrome fails to propagate the |method| parameter back | 258 // POST method, and chrome fails to propagate the |method| parameter back |
| 136 // to the server. This tests both that testserver.py does not succeed when | 259 // to the server. This tests both that testserver.py does not succeed when |
| 137 // it should fail, and this tests how the downloads extension api exposes | 260 // it should fail, and this tests how the downloads extension api exposes |
| 138 // the failure to extensions. | 261 // the failure to extensions. |
| 139 var downloadId = getNextId(); | 262 var downloadId = getNextId(); |
| 140 console.log(downloadId); | 263 console.debug(downloadId); |
| 141 | 264 |
| 142 var changedCompleted = chrome.test.callbackAdded(); | 265 var changedCompleted = chrome.test.callbackAdded(); |
| 143 function changedListener(delta) { | 266 function changedListener(delta) { |
| 144 console.log(delta.id); | 267 console.debug(delta.id); |
| 145 // Ignore onChanged events for downloads besides our own, or events that | 268 // Ignore onChanged events for downloads besides our own, or events that |
| 146 // signal any change besides interruption. | 269 // signal any change besides interruption. |
| 147 if ((delta.id != downloadId) || | 270 if ((delta.id != downloadId) || |
| 148 !delta.state || | 271 !delta.state || |
| 149 (delta.state.new != downloads.STATE_COMPLETE)) | 272 !delta.error) |
| 150 return; | 273 return; |
| 151 console.log(downloadId); | 274 chrome.test.assertEq(downloads.STATE_INTERRUPTED, delta.state.new); |
| 152 // TODO(benjhayden): Change COMPLETE to INTERRUPTED after | 275 chrome.test.assertEq(33, delta.error.new); |
| 153 // http://crbug.com/112342 | 276 console.debug(downloadId); |
| 154 downloads.search({id: downloadId}, | |
| 155 chrome.test.callback(function(items) { | |
| 156 console.log(downloadId); | |
| 157 chrome.test.assertEq(1, items.length); | |
| 158 chrome.test.assertEq(downloadId, items[0].id); | |
| 159 chrome.test.assertEq(0, items[0].totalBytes); | |
| 160 })); | |
| 161 downloads.onChanged.removeListener(changedListener); | 277 downloads.onChanged.removeListener(changedListener); |
| 162 changedCompleted(); | 278 if (changedCompleted) { |
| 279 changedCompleted(); | |
| 280 changedCompleted = null; | |
| 281 } | |
| 163 } | 282 } |
| 164 downloads.onChanged.addListener(changedListener); | 283 downloads.onChanged.addListener(changedListener); |
| 165 | 284 |
| 285 // Sometimes the DownloadsEventRouter detects the item for the first time | |
| 286 // after the item has already been interrupted. In this case, the | |
| 287 // onChanged event never fires, so run the changedListener manually. If | |
| 288 // the DownloadsEventRouter detects the item before it's interrupted, then | |
| 289 // the onChanged event should fire correctly. | |
| 290 var createdCompleted = chrome.test.callbackAdded(); | |
| 291 function createdListener(createdItem) { | |
| 292 console.debug(createdItem.id); | |
| 293 // Ignore events for any download besides our own. | |
| 294 if (createdItem.id != downloadId) | |
| 295 return; | |
| 296 console.debug(downloadId); | |
| 297 downloads.onCreated.removeListener(createdListener); | |
| 298 createdCompleted(); | |
| 299 if (createdItem.state == downloads.STATE_INTERRUPTED) { | |
| 300 changedListener({id: downloadId, state: {new: createdItem.state}, | |
| 301 error: {new: createdItem.error}}); | |
| 302 } | |
| 303 } | |
| 304 downloads.onCreated.addListener(createdListener); | |
| 305 | |
| 166 downloads.download( | 306 downloads.download( |
| 167 {'url': POST_URL, | 307 {'url': POST_URL, |
| 168 'filename': downloadId + '.txt', // Prevent 'file' danger. | 308 'filename': downloadId + '.txt', // Prevent 'file' danger. |
| 169 'body': 'BODY'}, | 309 'body': 'BODY'}, |
| 170 chrome.test.callback(function(id) { | 310 chrome.test.callback(function(id) { |
| 171 console.log(downloadId); | 311 console.debug(downloadId); |
| 172 chrome.test.assertEq(downloadId, id); | 312 chrome.test.assertEq(downloadId, id); |
| 173 })); | 313 })); |
| 174 }, | 314 }, |
| 175 | 315 |
| 176 function downloadPostWouldFailWithoutBody() { | 316 function downloadPostWouldFailWithoutBody() { |
| 177 // Test that downloadPostSuccess would fail if the resource requires the | 317 // Test that downloadPostSuccess would fail if the resource requires the |
| 178 // POST method and a request body, and chrome fails to propagate the | 318 // POST method and a request body, and chrome fails to propagate the |
| 179 // |body| parameter back to the server. This tests both that testserver.py | 319 // |body| parameter back to the server. This tests both that testserver.py |
| 180 // does not succeed when it should fail, and this tests how the downloads | 320 // does not succeed when it should fail, and this tests how the downloads |
| 181 // extension api exposes the failure to extensions. | 321 // extension api exposes the failure to extensions. |
| 182 var downloadId = getNextId(); | 322 var downloadId = getNextId(); |
| 183 console.log(downloadId); | 323 console.debug(downloadId); |
| 184 | 324 |
| 185 var changedCompleted = chrome.test.callbackAdded(); | 325 var changedCompleted = chrome.test.callbackAdded(); |
| 186 function changedListener(delta) { | 326 function changedListener(delta) { |
| 187 console.log(delta.id); | 327 console.debug(delta.id); |
| 188 // Ignore onChanged events for downloads besides our own, or events that | 328 // Ignore onChanged events for downloads besides our own, or events that |
| 189 // signal any change besides interruption. | 329 // signal any change besides interruption. |
| 190 if ((delta.id != downloadId) || | 330 if ((delta.id != downloadId) || |
| 191 !delta.state || | 331 !delta.state || |
| 192 (delta.state.new != downloads.STATE_COMPLETE)) | 332 !delta.error) |
| 193 return; | 333 return; |
| 194 console.log(downloadId); | 334 chrome.test.assertEq(downloads.STATE_INTERRUPTED, delta.state.new); |
| 195 // TODO(benjhayden): Change COMPLETE to INTERRUPTED after | 335 chrome.test.assertEq(33, delta.error.new); |
| 196 // http://crbug.com/112342 | 336 if (delta.error) console.debug(delta.error.new); |
| 197 downloads.search({id: downloadId}, | 337 console.debug(downloadId); |
| 198 chrome.test.callback(function(items) { | |
| 199 console.log(downloadId); | |
| 200 chrome.test.assertEq(1, items.length); | |
| 201 chrome.test.assertEq(downloadId, items[0].id); | |
| 202 chrome.test.assertEq(0, items[0].totalBytes); | |
| 203 })); | |
| 204 downloads.onChanged.removeListener(changedListener); | 338 downloads.onChanged.removeListener(changedListener); |
| 205 changedCompleted(); | 339 if (changedCompleted) { |
| 340 changedCompleted(); | |
| 341 changedCompleted = null; | |
| 342 } | |
| 206 } | 343 } |
| 207 downloads.onChanged.addListener(changedListener); | 344 downloads.onChanged.addListener(changedListener); |
| 208 | 345 |
| 346 // Sometimes the DownloadsEventRouter detects the item for the first time | |
| 347 // after the item has already been interrupted. In this case, the | |
| 348 // onChanged event never fires, so run the changedListener manually. If | |
| 349 // the DownloadsEventRouter detects the item before it's interrupted, then | |
| 350 // the onChanged event should fire correctly. | |
| 351 var createdCompleted = chrome.test.callbackAdded(); | |
| 352 function createdListener(createdItem) { | |
| 353 console.debug(createdItem.id); | |
| 354 // Ignore events for any download besides our own. | |
| 355 if (createdItem.id != downloadId) | |
| 356 return; | |
| 357 console.debug(downloadId); | |
| 358 downloads.onCreated.removeListener(createdListener); | |
| 359 createdCompleted(); | |
| 360 if (createdItem.state == downloads.STATE_INTERRUPTED) { | |
| 361 changedListener({id: downloadId, state: {new: createdItem.state}, | |
| 362 error: {new: createdItem.error}}); | |
| 363 } | |
| 364 } | |
| 365 downloads.onCreated.addListener(createdListener); | |
| 366 | |
| 209 downloads.download( | 367 downloads.download( |
| 210 {'url': POST_URL, | 368 {'url': POST_URL, |
| 211 'filename': downloadId + '.txt', // Prevent 'file' danger. | 369 'filename': downloadId + '.txt', // Prevent 'file' danger. |
| 212 'method': 'POST'}, | 370 'method': 'POST'}, |
| 213 chrome.test.callback(function(id) { | 371 chrome.test.callback(function(id) { |
| 214 console.log(downloadId); | 372 console.debug(downloadId); |
| 215 chrome.test.assertEq(downloadId, id); | 373 chrome.test.assertEq(downloadId, id); |
| 216 })); | 374 })); |
| 217 }, | 375 }, |
| 218 | 376 |
| 219 function downloadHeadersSuccess() { | 377 function downloadHeadersSuccess() { |
| 220 // Test the |header| download option. | 378 // Test the |header| download option. |
| 221 var downloadId = getNextId(); | 379 var downloadId = getNextId(); |
| 222 console.log(downloadId); | 380 console.debug(downloadId); |
| 223 var changedCompleted = chrome.test.callbackAdded(); | 381 var changedCompleted = chrome.test.callbackAdded(); |
| 224 function changedListener(delta) { | 382 function changedListener(delta) { |
| 225 console.log(delta.id); | 383 console.debug(delta.id); |
| 226 // Ignore onChanged events for downloads besides our own, or events that | 384 // Ignore onChanged events for downloads besides our own, or events that |
| 227 // signal any change besides completion. | 385 // signal any change besides completion. |
| 228 if ((delta.id != downloadId) || | 386 if ((delta.id != downloadId) || |
| 229 !delta.state || | 387 !delta.state) |
| 230 (delta.state.new != downloads.STATE_COMPLETE)) | |
| 231 return; | 388 return; |
| 232 console.log(downloadId); | 389 chrome.test.assertEq(downloads.STATE_COMPLETE, delta.state.new); |
| 390 console.debug(downloadId); | |
| 233 downloads.search({id: downloadId}, | 391 downloads.search({id: downloadId}, |
| 234 chrome.test.callback(function(items) { | 392 chrome.test.callback(function(items) { |
| 235 console.log(downloadId); | 393 console.debug(downloadId); |
| 236 chrome.test.assertEq(1, items.length); | 394 chrome.test.assertEq(1, items.length); |
| 237 chrome.test.assertEq(downloadId, items[0].id); | 395 chrome.test.assertEq(downloadId, items[0].id); |
| 396 debugObject(items[0]); | |
| 238 var EXPECTED_SIZE = 164; | 397 var EXPECTED_SIZE = 164; |
| 239 chrome.test.assertEq(EXPECTED_SIZE, items[0].totalBytes); | |
| 240 chrome.test.assertEq(EXPECTED_SIZE, items[0].fileSize); | |
| 241 chrome.test.assertEq(EXPECTED_SIZE, items[0].bytesReceived); | 398 chrome.test.assertEq(EXPECTED_SIZE, items[0].bytesReceived); |
| 242 })); | 399 })); |
| 243 downloads.onChanged.removeListener(changedListener); | 400 downloads.onChanged.removeListener(changedListener); |
| 244 changedCompleted(); | 401 changedCompleted(); |
| 245 } | 402 } |
| 246 downloads.onChanged.addListener(changedListener); | 403 downloads.onChanged.addListener(changedListener); |
| 247 | 404 |
| 248 downloads.download( | 405 downloads.download( |
| 249 {'url': HEADERS_URL, | 406 {'url': HEADERS_URL, |
| 250 'filename': downloadId + '.txt', // Prevent 'file' danger. | 407 'filename': downloadId + '.txt', // Prevent 'file' danger. |
| 251 'headers': [{'name': 'Foo', 'value': 'bar'}, | 408 'headers': [{'name': 'Foo', 'value': 'bar'}, |
| 252 {'name': 'Qx', 'value': 'yo'}]}, | 409 {'name': 'Qx', 'value': 'yo'}]}, |
| 253 chrome.test.callback(function(id) { | 410 chrome.test.callback(function(id) { |
| 254 console.log(downloadId); | 411 console.debug(downloadId); |
| 255 chrome.test.assertEq(downloadId, id); | 412 chrome.test.assertEq(downloadId, id); |
| 256 })); | 413 })); |
| 257 }, | 414 }, |
| 258 | 415 |
| 259 function downloadHeadersWouldFail() { | 416 function downloadHeadersWouldFail() { |
| 260 // Test that downloadHeadersSuccess() would fail if the resource requires | 417 // Test that downloadHeadersSuccess() would fail if the resource requires |
| 261 // the headers, and chrome fails to propagate them back to the server. | 418 // the headers, and chrome fails to propagate them back to the server. |
| 262 // This tests both that testserver.py does not succeed when it should | 419 // This tests both that testserver.py does not succeed when it should |
| 263 // fail as well as how the downloads extension api exposes the | 420 // fail as well as how the downloads extension api exposes the |
| 264 // failure to extensions. | 421 // failure to extensions. |
| 265 var downloadId = getNextId(); | 422 var downloadId = getNextId(); |
| 266 console.log(downloadId); | 423 console.debug(downloadId); |
| 267 | 424 |
| 268 var changedCompleted = chrome.test.callbackAdded(); | 425 var changedCompleted = chrome.test.callbackAdded(); |
| 269 function changedListener(delta) { | 426 function changedListener(delta) { |
| 270 console.log(delta.id); | 427 console.debug(delta.id); |
| 271 // Ignore onChanged events for downloads besides our own, or events that | 428 // Ignore onChanged events for downloads besides our own, or events that |
| 272 // signal any change besides interruption. | 429 // signal any change besides interruption. |
| 273 if ((delta.id != downloadId) || | 430 if ((delta.id != downloadId) || |
| 274 !delta.state || | 431 !delta.state || |
| 275 (delta.state.new != downloads.STATE_COMPLETE)) | 432 !delta.error) |
| 276 return; | 433 return; |
| 277 console.log(downloadId); | 434 chrome.test.assertEq(downloads.STATE_INTERRUPTED, delta.state.new); |
| 278 // TODO(benjhayden): Change COMPLETE to INTERRUPTED after | 435 chrome.test.assertEq(33, delta.error.new); |
| 279 // http://crbug.com/112342 | 436 console.debug(downloadId); |
| 280 downloads.search({id: downloadId}, | |
| 281 chrome.test.callback(function(items) { | |
| 282 console.log(downloadId); | |
| 283 chrome.test.assertEq(1, items.length); | |
| 284 chrome.test.assertEq(downloadId, items[0].id); | |
| 285 chrome.test.assertEq(0, items[0].totalBytes); | |
| 286 })); | |
| 287 downloads.onChanged.removeListener(changedListener); | 437 downloads.onChanged.removeListener(changedListener); |
| 288 changedCompleted(); | 438 if (changedCompleted) { |
| 439 changedCompleted(); | |
| 440 changedCompleted = null; | |
| 441 } | |
| 289 } | 442 } |
| 290 downloads.onChanged.addListener(changedListener); | 443 downloads.onChanged.addListener(changedListener); |
| 291 | 444 |
| 445 // Sometimes the DownloadsEventRouter detects the item for the first time | |
| 446 // after the item has already been interrupted. In this case, the | |
| 447 // onChanged event never fires, so run the changedListener manually. If | |
| 448 // the DownloadsEventRouter detects the item before it's interrupted, then | |
| 449 // the onChanged event should fire correctly. | |
| 450 var createdCompleted = chrome.test.callbackAdded(); | |
| 451 function createdListener(createdItem) { | |
| 452 console.debug(createdItem.id); | |
| 453 // Ignore events for any download besides our own. | |
| 454 if (createdItem.id != downloadId) | |
| 455 return; | |
| 456 console.debug(downloadId); | |
| 457 downloads.onCreated.removeListener(createdListener); | |
| 458 createdCompleted(); | |
| 459 if (createdItem.state == downloads.STATE_INTERRUPTED) { | |
| 460 changedListener({id: downloadId, state: {new: createdItem.state}, | |
| 461 error: {new: createdItem.error}}); | |
| 462 } | |
| 463 } | |
| 464 downloads.onCreated.addListener(createdListener); | |
| 465 | |
| 292 downloads.download( | 466 downloads.download( |
| 293 {'url': HEADERS_URL}, | 467 {'url': HEADERS_URL}, |
| 294 chrome.test.callback(function(id) { | 468 chrome.test.callback(function(id) { |
| 295 console.log(downloadId); | 469 console.debug(downloadId); |
| 296 chrome.test.assertEq(downloadId, id); | 470 chrome.test.assertEq(downloadId, id); |
| 297 })); | 471 })); |
| 298 }, | 472 }, |
| 299 | 473 |
| 300 function downloadInterrupted() { | 474 function downloadInterrupted() { |
| 301 // Test that cancel()ing an in-progress download causes its state to | 475 // Test that cancel()ing an in-progress download causes its state to |
| 302 // transition to interrupted, and test that that state transition is | 476 // transition to interrupted, and test that that state transition is |
| 303 // detectable by an onChanged event listener. | 477 // detectable by an onChanged event listener. |
| 304 // TODO(benjhayden): Test other sources of interruptions such as server | 478 // TODO(benjhayden): Test other sources of interruptions such as server |
| 305 // death. | 479 // death. |
| 306 var downloadId = getNextId(); | 480 var downloadId = getNextId(); |
| 307 console.log(downloadId); | 481 console.debug(downloadId); |
| 308 | 482 |
| 309 var createdCompleted = chrome.test.callbackAdded(); | 483 var createdCompleted = chrome.test.callbackAdded(); |
| 310 function createdListener(createdItem) { | 484 function createdListener(createdItem) { |
| 311 console.log(createdItem.id); | 485 console.debug(createdItem.id); |
| 312 // Ignore onCreated events for any download besides our own. | 486 // Ignore onCreated events for any download besides our own. |
| 313 if (createdItem.id != downloadId) | 487 if (createdItem.id != downloadId) |
| 314 return; | 488 return; |
| 315 console.log(downloadId); | 489 console.debug(downloadId); |
| 316 // TODO(benjhayden) Move this cancel() into the download() callback | 490 // TODO(benjhayden) Move this cancel() into the download() callback |
| 317 // after ensuring that DownloadItems are created before that callback | 491 // after ensuring that DownloadItems are created before that callback |
| 318 // is fired. | 492 // is fired. |
| 319 downloads.cancel(downloadId, chrome.test.callback(function() { | 493 downloads.cancel(downloadId, chrome.test.callback(function() { |
| 320 console.log(downloadId); | 494 console.debug(downloadId); |
| 321 })); | 495 })); |
| 322 downloads.onCreated.removeListener(createdListener); | 496 downloads.onCreated.removeListener(createdListener); |
| 323 createdCompleted(); | 497 createdCompleted(); |
| 324 } | 498 } |
| 325 downloads.onCreated.addListener(createdListener); | 499 downloads.onCreated.addListener(createdListener); |
| 326 | 500 |
| 327 var changedCompleted = chrome.test.callbackAdded(); | 501 var changedCompleted = chrome.test.callbackAdded(); |
| 328 function changedListener(delta) { | 502 function changedListener(delta) { |
| 329 console.log(delta.id); | 503 console.debug(delta.id); |
| 330 // Ignore onChanged events for downloads besides our own, or events that | 504 // Ignore onChanged events for downloads besides our own, or events that |
| 331 // signal any change besides interruption. | 505 // signal any change besides interruption. |
| 332 if ((delta.id != downloadId) || | 506 if ((delta.id != downloadId) || |
| 333 !delta.state || | 507 !delta.state || |
| 334 (delta.state.new != downloads.STATE_INTERRUPTED) || | 508 !delta.error) |
| 335 !delta.error || | |
| 336 (delta.error.new != 40)) | |
| 337 return; | 509 return; |
| 338 console.log(downloadId); | 510 chrome.test.assertEq(downloads.STATE_INTERRUPTED, delta.state.new); |
| 511 chrome.test.assertEq(40, delta.error.new); | |
| 512 console.debug(downloadId); | |
| 339 downloads.onChanged.removeListener(changedListener); | 513 downloads.onChanged.removeListener(changedListener); |
| 340 changedCompleted(); | 514 changedCompleted(); |
| 341 } | 515 } |
| 342 downloads.onChanged.addListener(changedListener); | 516 downloads.onChanged.addListener(changedListener); |
| 343 | 517 |
| 344 downloads.download( | 518 downloads.download( |
| 345 {'url': NEVER_FINISH_URL}, | 519 {'url': NEVER_FINISH_URL}, |
| 346 chrome.test.callback(function(id) { | 520 chrome.test.callback(function(id) { |
| 347 console.log(downloadId); | 521 console.debug(downloadId); |
| 348 chrome.test.assertEq(downloadId, id); | 522 chrome.test.assertEq(downloadId, id); |
| 349 })); | 523 })); |
| 350 }, | 524 }, |
| 351 | 525 |
| 352 function downloadOnChanged() { | |
| 353 // Test that download completion is detectable by an onChanged event | |
| 354 // listener. | |
| 355 var downloadId = getNextId(); | |
| 356 console.log(downloadId); | |
| 357 var callbackCompleted = chrome.test.callbackAdded(); | |
| 358 function myListener(delta) { | |
| 359 console.log(delta.id); | |
| 360 if ((delta.id != downloadId) || | |
| 361 !delta.state || | |
| 362 (delta.state.new != downloads.STATE_COMPLETE)) | |
| 363 return; | |
| 364 console.log(downloadId); | |
| 365 downloads.onChanged.removeListener(myListener); | |
| 366 callbackCompleted(); | |
| 367 } | |
| 368 downloads.onChanged.addListener(myListener); | |
| 369 downloads.download( | |
| 370 {"url": SAFE_FAST_URL}, | |
| 371 chrome.test.callback(function(id) { | |
| 372 console.log(downloadId); | |
| 373 chrome.test.assertEq(downloadId, id); | |
| 374 })); | |
| 375 }, | |
| 376 | |
| 377 function downloadFilename() { | 526 function downloadFilename() { |
| 378 // Test that we can suggest a filename for a new download, and test that | 527 // Test that we can suggest a filename for a new download, and test that |
| 379 // we can detect filename changes with an onChanged event listener. | 528 // we can detect filename changes with an onChanged event listener. |
| 380 var FILENAME = 'owiejtoiwjrfoiwjroiwjroiwjroiwjrfi'; | 529 var FILENAME = 'owiejtoiwjrfoiwjroiwjroiwjroiwjrfi'; |
| 381 var downloadId = getNextId(); | 530 var downloadId = getNextId(); |
| 382 console.log(downloadId); | 531 console.debug(downloadId); |
| 383 var callbackCompleted = chrome.test.callbackAdded(); | 532 var callbackCompleted = chrome.test.callbackAdded(); |
| 384 function myListener(delta) { | 533 function myListener(delta) { |
| 385 console.log(delta.id); | 534 console.debug(delta.id); |
| 386 if ((delta.id != downloadId) || | 535 if ((delta.id != downloadId) || |
| 387 !delta.filename || | 536 !delta.filename || |
| 388 (delta.filename.new.indexOf(FILENAME) == -1)) | 537 (delta.filename.new.indexOf(FILENAME) == -1)) |
| 389 return; | 538 return; |
| 390 console.log(downloadId); | 539 console.debug(downloadId); |
| 391 downloads.onChanged.removeListener(myListener); | 540 downloads.onChanged.removeListener(myListener); |
| 392 callbackCompleted(); | 541 callbackCompleted(); |
| 393 } | 542 } |
| 394 downloads.onChanged.addListener(myListener); | 543 downloads.onChanged.addListener(myListener); |
| 395 downloads.download( | 544 downloads.download( |
| 396 {'url': SAFE_FAST_URL, 'filename': FILENAME}, | 545 {'url': SAFE_FAST_URL, 'filename': FILENAME}, |
| 397 chrome.test.callback(function(id) { | 546 chrome.test.callback(function(id) { |
| 398 console.log(downloadId); | 547 console.debug(downloadId); |
| 399 chrome.test.assertEq(downloadId, id); | 548 chrome.test.assertEq(downloadId, id); |
| 400 })); | 549 })); |
| 401 }, | 550 }, |
| 402 | 551 |
| 403 function downloadOnCreated() { | 552 function downloadOnCreated() { |
| 404 // Test that the onCreated event fires when we start a download. | 553 // Test that the onCreated event fires when we start a download. |
| 405 var downloadId = getNextId(); | 554 var downloadId = getNextId(); |
| 406 console.log(downloadId); | 555 console.debug(downloadId); |
| 407 var createdCompleted = chrome.test.callbackAdded(); | 556 var createdCompleted = chrome.test.callbackAdded(); |
| 408 function createdListener(item) { | 557 function createdListener(item) { |
| 409 console.log(item.id); | 558 console.debug(item.id); |
| 410 if (item.id != downloadId) | 559 if (item.id != downloadId) |
| 411 return; | 560 return; |
| 412 console.log(downloadId); | 561 console.debug(downloadId); |
| 413 createdCompleted(); | 562 createdCompleted(); |
| 414 downloads.onCreated.removeListener(createdListener); | 563 downloads.onCreated.removeListener(createdListener); |
| 415 }; | 564 }; |
| 416 downloads.onCreated.addListener(createdListener); | 565 downloads.onCreated.addListener(createdListener); |
| 417 downloads.download( | 566 downloads.download( |
| 418 {'url': SAFE_FAST_URL}, | 567 {'url': SAFE_FAST_URL}, |
| 419 chrome.test.callback(function(id) { | 568 chrome.test.callback(function(id) { |
| 420 console.log(downloadId); | 569 console.debug(downloadId); |
| 421 chrome.test.assertEq(downloadId, id); | 570 chrome.test.assertEq(downloadId, id); |
| 422 })); | 571 })); |
| 423 }, | 572 }, |
| 424 | 573 |
| 425 function downloadInvalidFilename() { | 574 function downloadInvalidFilename() { |
| 426 // Test that we disallow invalid filenames for new downloads. | 575 // Test that we disallow invalid filenames for new downloads. |
| 427 downloads.download( | 576 downloads.download( |
| 428 {'url': SAFE_FAST_URL, 'filename': '../../../../../etc/passwd'}, | 577 {'url': SAFE_FAST_URL, 'filename': '../../../../../etc/passwd'}, |
| 429 chrome.test.callbackFail(downloads.ERROR_GENERIC)); | 578 chrome.test.callbackFail(downloads.ERROR_GENERIC)); |
| 430 }, | 579 }, |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 499 // Test that download() requires a valid url, including protocol and | 648 // Test that download() requires a valid url, including protocol and |
| 500 // hostname. | 649 // hostname. |
| 501 downloads.download( | 650 downloads.download( |
| 502 {'url': 'foo/bar.html#frag'}, | 651 {'url': 'foo/bar.html#frag'}, |
| 503 chrome.test.callbackFail(downloads.ERROR_INVALID_URL)); | 652 chrome.test.callbackFail(downloads.ERROR_INVALID_URL)); |
| 504 }, | 653 }, |
| 505 | 654 |
| 506 function downloadAllowFragments() { | 655 function downloadAllowFragments() { |
| 507 // Valid URLs plus fragments are still valid URLs. | 656 // Valid URLs plus fragments are still valid URLs. |
| 508 var downloadId = getNextId(); | 657 var downloadId = getNextId(); |
| 509 console.log(downloadId); | 658 console.debug(downloadId); |
| 510 downloads.download( | 659 downloads.download( |
| 511 {'url': SAFE_FAST_URL + '#frag'}, | 660 {'url': SAFE_FAST_URL + '#frag'}, |
| 512 chrome.test.callback(function(id) { | 661 chrome.test.callback(function(id) { |
| 513 chrome.test.assertEq(downloadId, id); | 662 chrome.test.assertEq(downloadId, id); |
| 514 })); | 663 })); |
| 515 }, | 664 }, |
| 516 | 665 |
| 517 function downloadAllowDataURLs() { | 666 function downloadAllowDataURLs() { |
| 518 // Valid data URLs are valid URLs. | 667 // Valid data URLs are valid URLs. |
| 519 var downloadId = getNextId(); | 668 var downloadId = getNextId(); |
| 520 console.log(downloadId); | 669 console.debug(downloadId); |
| 521 downloads.download( | 670 downloads.download( |
| 522 {'url': 'data:text/plain,hello'}, | 671 {'url': 'data:text/plain,hello'}, |
| 523 chrome.test.callback(function(id) { | 672 chrome.test.callback(function(id) { |
| 524 chrome.test.assertEq(downloadId, id); | 673 chrome.test.assertEq(downloadId, id); |
| 525 })); | 674 })); |
| 526 }, | 675 }, |
| 527 | 676 |
| 528 function downloadAllowFileURLs() { | 677 function downloadAllowFileURLs() { |
| 529 // Valid file URLs are valid URLs. | 678 // Valid file URLs are valid URLs. |
| 530 var downloadId = getNextId(); | 679 var downloadId = getNextId(); |
| 531 console.log(downloadId); | 680 console.debug(downloadId); |
| 532 downloads.download( | 681 downloads.download( |
| 533 {'url': 'file:///'}, | 682 {'url': 'file:///'}, |
| 534 chrome.test.callback(function(id) { | 683 chrome.test.callback(function(id) { |
| 535 chrome.test.assertEq(downloadId, id); | 684 chrome.test.assertEq(downloadId, id); |
| 536 })); | 685 })); |
| 537 }, | 686 }, |
| 538 | 687 |
| 539 // TODO(benjhayden): Set up a test ftp server. | 688 // TODO(benjhayden): Set up a test ftp server. |
| 540 // function downloadAllowFTPURLs() { | 689 // function downloadAllowFTPURLs() { |
| 541 // // Valid ftp URLs are valid URLs. | 690 // // Valid ftp URLs are valid URLs. |
| 542 // var downloadId = getNextId(); | 691 // var downloadId = getNextId(); |
| 543 // console.log(downloadId); | 692 // console.debug(downloadId); |
| 544 // downloads.download( | 693 // downloads.download( |
| 545 // {'url': 'ftp://localhost:' + testConfig.testServer.port + '/'}, | 694 // {'url': 'ftp://localhost:' + testConfig.testServer.port + '/'}, |
| 546 // chrome.test.callback(function(id) { | 695 // chrome.test.callback(function(id) { |
| 547 // chrome.test.assertEq(downloadId, id); | 696 // chrome.test.assertEq(downloadId, id); |
| 548 // })); | 697 // })); |
| 549 // }, | 698 // }, |
| 550 | 699 |
| 551 function downloadInvalidURL7() { | 700 function downloadInvalidURL7() { |
| 552 // Test that download() rejects javascript urls. | 701 // Test that download() rejects javascript urls. |
| 553 downloads.download( | 702 downloads.download( |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 616 function downloadResumeInvalidType() { | 765 function downloadResumeInvalidType() { |
| 617 assertThrows(('Invalid value for argument 1. Expected \'integer\' ' + | 766 assertThrows(('Invalid value for argument 1. Expected \'integer\' ' + |
| 618 'but got \'string\'.'), | 767 'but got \'string\'.'), |
| 619 downloads.resume, | 768 downloads.resume, |
| 620 'foo'); | 769 'foo'); |
| 621 }, | 770 }, |
| 622 | 771 |
| 623 function downloadCancelInvalidId() { | 772 function downloadCancelInvalidId() { |
| 624 // Canceling a non-existent download is not considered an error. | 773 // Canceling a non-existent download is not considered an error. |
| 625 downloads.cancel(-42, chrome.test.callback(function() { | 774 downloads.cancel(-42, chrome.test.callback(function() { |
| 626 console.log(''); | 775 console.debug(''); |
| 627 })); | 776 })); |
| 628 }, | 777 }, |
| 629 | 778 |
| 630 function downloadCancelInvalidType() { | 779 function downloadCancelInvalidType() { |
| 631 assertThrows(('Invalid value for argument 1. Expected \'integer\' ' + | 780 assertThrows(('Invalid value for argument 1. Expected \'integer\' ' + |
| 632 'but got \'string\'.'), | 781 'but got \'string\'.'), |
| 633 downloads.cancel, 'foo'); | 782 downloads.cancel, 'foo'); |
| 634 }, | 783 }, |
| 635 | 784 |
| 636 function downloadNoComplete() { | 785 function downloadNoComplete() { |
| 637 // This is used partly to test cleanUp. | 786 // This is used partly to test cleanUp. |
| 638 var downloadId = getNextId(); | 787 var downloadId = getNextId(); |
| 639 console.log(downloadId); | 788 console.debug(downloadId); |
| 640 downloads.download( | 789 downloads.download( |
| 641 {'url': NEVER_FINISH_URL}, | 790 {'url': NEVER_FINISH_URL}, |
| 642 chrome.test.callback(function(id) { | 791 chrome.test.callback(function(id) { |
| 643 console.log(downloadId); | 792 console.debug(downloadId); |
| 644 chrome.test.assertEq(downloadId, id); | 793 chrome.test.assertEq(downloadId, id); |
| 645 })); | 794 })); |
| 646 }, | 795 }, |
| 647 | 796 |
| 648 function cleanUp() { | 797 function cleanUp() { |
| 649 // cleanUp must come last. It clears out all in-progress downloads | 798 // cleanUp must come last. It clears out all in-progress downloads |
| 650 // so the browser can shutdown cleanly. | 799 // so the browser can shutdown cleanly. |
| 651 console.log(nextId); | 800 console.debug(nextId); |
| 652 function makeCallback(id) { | 801 function makeCallback(id) { |
| 653 return function() { | 802 return function() { |
| 654 console.log(id); | 803 console.debug(id); |
| 655 } | 804 } |
| 656 } | 805 } |
| 657 for (var id = 0; id < nextId; ++id) { | 806 for (var id = 0; id < nextId; ++id) { |
| 658 downloads.cancel(id, chrome.test.callback(makeCallback(id))); | 807 downloads.cancel(id, chrome.test.callback(makeCallback(id))); |
| 659 } | 808 } |
| 660 }, | 809 }, |
| 661 | 810 |
| 662 function callNotifyPass() { | 811 function callNotifyPass() { |
| 663 chrome.test.notifyPass(); | 812 chrome.test.notifyPass(); |
| 664 setTimeout(chrome.test.callback(function() { | 813 setTimeout(chrome.test.callback(function() { |
| 665 console.log(''); | 814 console.debug(''); |
| 666 }), 0); | 815 }), 0); |
| 667 } | 816 } |
| 668 ]); | 817 ]); |
| 669 }); | 818 }); |
| OLD | NEW |