1// Copyright (C) 2019 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import {assertTrue} from './logging'; 16 17export function fetchWithTimeout( 18 input: RequestInfo, 19 init: RequestInit, 20 timeoutMs: number, 21) { 22 return new Promise<Response>((resolve, reject) => { 23 const timer = setTimeout( 24 () => 25 reject(new Error(`fetch(${input}) timed out after ${timeoutMs} ms`)), 26 timeoutMs, 27 ); 28 fetch(input, init) 29 .then((response) => resolve(response)) 30 .catch((err) => reject(err)) 31 .finally(() => clearTimeout(timer)); 32 }); 33} 34 35export function fetchWithProgress( 36 url: string, 37 onProgress?: (percentage: number) => void, 38): Promise<Blob> { 39 return new Promise((resolve, reject) => { 40 const xhr = new XMLHttpRequest(); 41 42 xhr.open('GET', url, /* async= */ true); 43 xhr.responseType = 'blob'; 44 45 xhr.onprogress = (event) => { 46 if (event.lengthComputable) { 47 const percentComplete = Math.round((event.loaded / event.total) * 100); 48 onProgress?.(percentComplete); 49 } 50 }; 51 52 xhr.onload = () => { 53 if (xhr.status >= 200 && xhr.status < 300) { 54 resolve(xhr.response); // Resolve with the Blob response 55 } else { 56 reject( 57 new Error(`Failed to download: ${xhr.status} ${xhr.statusText}`), 58 ); 59 } 60 }; 61 62 xhr.onerror = () => { 63 reject(new Error(`Network error in fetchWithProgress(${url})`)); 64 }; 65 66 xhr.send(); 67 }); 68} 69 70/** 71 * NOTE: this function can only be called from synchronous contexts. It will 72 * fail if called in timer handlers or async continuations (e.g. after an await) 73 * Use assetSrc(relPath) which caches it on startup. 74 * @returns the directory where the app is served from, e.g. 'v46.0-a2082649b' 75 */ 76export function getServingRoot() { 77 // Works out the root directory where the content should be served from 78 // e.g. `http://origin/v1.2.3/`. 79 const script = document.currentScript as HTMLScriptElement; 80 81 if (script === null) { 82 // Can be null in tests. 83 assertTrue(typeof jest !== 'undefined'); 84 return ''; 85 } 86 87 let root = script.src; 88 root = root.substring(0, root.lastIndexOf('/') + 1); 89 return root; 90} 91