diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/404.html | 52 | ||||
-rw-r--r-- | app/error.html | 79 | ||||
-rw-r--r-- | app/images/enslogo.svg | 1 | ||||
-rw-r--r-- | app/loading.html | 13 | ||||
-rw-r--r-- | app/scripts/lib/ens-ipfs/contracts/registry.js (renamed from app/scripts/lib/ens-ipfs/contracts/registrar.js) | 0 | ||||
-rw-r--r-- | app/scripts/lib/ens-ipfs/contracts/resolver.js | 2 | ||||
-rw-r--r-- | app/scripts/lib/ens-ipfs/resolver.js | 52 | ||||
-rw-r--r-- | app/scripts/lib/ens-ipfs/setup.js | 30 |
8 files changed, 54 insertions, 175 deletions
diff --git a/app/404.html b/app/404.html deleted file mode 100644 index 8a6df9d7a..000000000 --- a/app/404.html +++ /dev/null @@ -1,52 +0,0 @@ -<html> -<head> - <title>MetaMask</title> - <style> - *{ - padding: 0; - margin: 0; - box-sizing: border-box; - } - img{ - display: block; - } - html, body{ - display: flex; - justify-content: center; - align-items: center; - width: 100%; - height: 100%; - } - .app{ - position: relative; - width: 100%; - height: auto; - overflow: hidden; - } - img{ - display: block; - width: 100%; - height: auto; - } - h2{ - display: block; - width: 100%; - overflow: hidden; - position: absolute; - bottom: 20%; - left: 0; - color: #1b243d; - text-align: center; - } - h2 > a{ - color: #1b243d; - } - </style> -</head> -<body> - <div class="app"> - <img src="./images/404.png" alt=""> - <h2>Powered by <a href="https://www.portal.network/">Portal Network</a></h2> - </div> -</body> -</html> diff --git a/app/error.html b/app/error.html deleted file mode 100644 index 366b3d94a..000000000 --- a/app/error.html +++ /dev/null @@ -1,79 +0,0 @@ -<html> -<head> - <title>MetaMask Error</title> - <link href="https://fonts.googleapis.com/css?family=Rokkitt" rel="stylesheet"> - <style> - *{ - padding: 0; - margin: 0; - box-sizing: border-box; - } - img{ - display: block; - } - html, body{ - display: flex; - justify-content: center; - align-items: center; - width: 100%; - height: 100%; - } - @keyframes logoAmin{ - from {transform: scale(1);} - 50%{transform: scale(1.1);} - to {transform: scale(1);} - } - .errorBox{ - width: 70%; - height: auto; - overflow: hidden; - background-image: url("./images/deadface.png"); - background-repeat: no-repeat; - background-position: 100% 50%; - background-size: auto 90%; - padding: 5px; - } - .errorBox > img{ - width: 100px; - height: auto; - margin-bottom: 25px; - animation: logoAmin 1s infinite linear; - } - .errorBox > h1, .errorBox > h2{ - letter-spacing: 2px; - } - .errorBox > h1{ - color: #9b9b9b; - font-size: 40px; - } - .errorBox > h2{ - color: #1b243d; - font-size: 20px; - padding-top: 5px; - } - .errorBox > h2 >a{ - color: #1b243d; - } - .errorBox > h2 >a:hover{ - color: #44588e; - } - - .errorBox > h1 > span{ - color: #33559f; - } - - </style> -</head> -<body> - <div class="errorBox"> - <img src="./images/logo.png" alt=""> - <h1><span id="name"></span> not found</h1> - <h2>Powered by <a href="https://www.portal.network/">Portal Network</a></h2> - </div> - <script> - let index = location.href.lastIndexOf("?name=") - let name = location.href.slice(index + 6) - document.getElementById("name").innerHTML = name - </script> -</body> -</html> diff --git a/app/images/enslogo.svg b/app/images/enslogo.svg new file mode 100644 index 000000000..20d94c0b1 --- /dev/null +++ b/app/images/enslogo.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 72.52 80.95"><defs><style>.cls-3{fill:#a0a8d4}</style><linearGradient id="linear-gradient" x1="41.95" y1="2.57" x2="12.57" y2="34.42" gradientUnits="userSpaceOnUse"><stop offset=".58" stop-color="#a0a8d4"/><stop offset=".73" stop-color="#8791c7"/><stop offset=".91" stop-color="#6470b4"/></linearGradient><linearGradient id="linear-gradient-2" x1="42.57" y1="81.66" x2="71.96" y2="49.81" xlink:href="#linear-gradient"/><linearGradient id="linear-gradient-3" x1="42.26" y1="1.24" x2="42.26" y2="82.84" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#513eff"/><stop offset=".18" stop-color="#5157ff"/><stop offset=".57" stop-color="#5298ff"/><stop offset="1" stop-color="#52e5ff"/></linearGradient></defs><g style="isolation:isolate"><g id="Layer_1" data-name="Layer 1"><path d="M15.28 34.39c.8 1.71 2.78 5.09 2.78 5.09L40.95 1.64l-22.34 15.6a9.75 9.75 0 0 0-3.18 3.5 16.19 16.19 0 0 0-.15 13.65z" transform="translate(-6 -1.64)" fill="url(#linear-gradient)"/><path class="cls-3" d="M6.21 46.85a25.47 25.47 0 0 0 10 18.51l24.71 17.23s-15.46-22.28-28.5-44.45a22.39 22.39 0 0 1-2.62-7.56 12.1 12.1 0 0 1 0-3.63c-.34.63-1 1.92-1 1.92a29.35 29.35 0 0 0-2.67 8.55 52.28 52.28 0 0 0 .08 9.43z" transform="translate(-6 -1.64)"/><path d="M69.25 49.84c-.8-1.71-2.78-5.09-2.78-5.09L43.58 82.59 65.92 67a9.75 9.75 0 0 0 3.18-3.5 16.19 16.19 0 0 0 .15-13.66z" transform="translate(-6 -1.64)" fill="url(#linear-gradient-2)"/><path class="cls-3" d="M78.32 37.38a25.47 25.47 0 0 0-10-18.51L43.61 1.64s15.45 22.28 28.5 44.45a22.39 22.39 0 0 1 2.61 7.56 12.1 12.1 0 0 1 0 3.63c.34-.63 1-1.92 1-1.92a29.35 29.35 0 0 0 2.67-8.55 52.28 52.28 0 0 0-.07-9.43z" transform="translate(-6 -1.64)"/><path d="M15.43 20.74a9.75 9.75 0 0 1 3.18-3.5l22.34-15.6-22.89 37.85s-2-3.38-2.78-5.09a16.19 16.19 0 0 1 .15-13.66zM6.21 46.85a25.47 25.47 0 0 0 10 18.51l24.71 17.23s-15.46-22.28-28.5-44.45a22.39 22.39 0 0 1-2.62-7.56 12.1 12.1 0 0 1 0-3.63c-.34.63-1 1.92-1 1.92a29.35 29.35 0 0 0-2.67 8.55 52.28 52.28 0 0 0 .08 9.43zm63 3c-.8-1.71-2.78-5.09-2.78-5.09L43.58 82.59 65.92 67a9.75 9.75 0 0 0 3.18-3.5 16.19 16.19 0 0 0 .15-13.66zm9.07-12.46a25.47 25.47 0 0 0-10-18.51L43.61 1.64s15.45 22.28 28.5 44.45a22.39 22.39 0 0 1 2.61 7.56 12.1 12.1 0 0 1 0 3.63c.34-.63 1-1.92 1-1.92a29.35 29.35 0 0 0 2.67-8.55 52.28 52.28 0 0 0-.07-9.43z" transform="translate(-6 -1.64)" style="mix-blend-mode:color" fill="url(#linear-gradient-3)"/></g></g></svg>
\ No newline at end of file diff --git a/app/loading.html b/app/loading.html index 71403a5ac..3b896b718 100644 --- a/app/loading.html +++ b/app/loading.html @@ -11,10 +11,10 @@ top: 50%; left: 50%; transform: translate(-50%, -50%); - width: 256px; + text-align: center; } #logo { - width: 100%; + width: 256px; animation: pulse 1s ease-in-out infinite; } @keyframes pulse { @@ -33,13 +33,8 @@ </head> <body> <div id="div-logo"> - <img id="logo" src="./images/loginglogo.svg"> + <img id="logo" src="./images/enslogo.svg"> + <h1 class="center">MetaMask is querying ENS ...</h1> </div> - <script type="text/javascript"> - // redirect to 404 after one minute - setTimeout(() => { - location.href = './404.html' - }, 60000) - </script> </body> </html> diff --git a/app/scripts/lib/ens-ipfs/contracts/registrar.js b/app/scripts/lib/ens-ipfs/contracts/registry.js index 99ca24458..99ca24458 100644 --- a/app/scripts/lib/ens-ipfs/contracts/registrar.js +++ b/app/scripts/lib/ens-ipfs/contracts/registry.js diff --git a/app/scripts/lib/ens-ipfs/contracts/resolver.js b/app/scripts/lib/ens-ipfs/contracts/resolver.js index 1bf3f90ce..b61fbed88 100644 --- a/app/scripts/lib/ens-ipfs/contracts/resolver.js +++ b/app/scripts/lib/ens-ipfs/contracts/resolver.js @@ -1,2 +1,2 @@ module.exports = -[{'constant': true, 'inputs': [{'name': 'interfaceID', 'type': 'bytes4'}], 'name': 'supportsInterface', 'outputs': [{'name': '', 'type': 'bool'}], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'contentTypes', 'type': 'uint256'}], 'name': 'ABI', 'outputs': [{'name': 'contentType', 'type': 'uint256'}, {'name': 'data', 'type': 'bytes'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'x', 'type': 'bytes32'}, {'name': 'y', 'type': 'bytes32'}], 'name': 'setPubkey', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'content', 'outputs': [{'name': 'ret', 'type': 'bytes32'}], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'addr', 'outputs': [{'name': 'ret', 'type': 'address'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'contentType', 'type': 'uint256'}, {'name': 'data', 'type': 'bytes'}], 'name': 'setABI', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'name', 'outputs': [{'name': 'ret', 'type': 'string'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'name', 'type': 'string'}], 'name': 'setName', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'hash', 'type': 'bytes32'}], 'name': 'setContent', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'pubkey', 'outputs': [{'name': 'x', 'type': 'bytes32'}, {'name': 'y', 'type': 'bytes32'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'addr', 'type': 'address'}], 'name': 'setAddr', 'outputs': [], 'payable': false, 'type': 'function'}, {'inputs': [{'name': 'ensAddr', 'type': 'address'}], 'payable': false, 'type': 'constructor'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'a', 'type': 'address'}], 'name': 'AddrChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'hash', 'type': 'bytes32'}], 'name': 'ContentChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'name', 'type': 'string'}], 'name': 'NameChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': true, 'name': 'contentType', 'type': 'uint256'}], 'name': 'ABIChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'x', 'type': 'bytes32'}, {'indexed': false, 'name': 'y', 'type': 'bytes32'}], 'name': 'PubkeyChanged', 'type': 'event'}] +[{'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'hash', 'type': 'bytes32'}], 'name': 'setContent', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'content', 'outputs': [{'name': '', 'type': 'bytes32'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'interfaceID', 'type': 'bytes4'}], 'name': 'supportsInterface', 'outputs': [{'name': '', 'type': 'bool'}], 'payable': false, 'stateMutability': 'pure', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'key', 'type': 'string'}, {'name': 'value', 'type': 'string'}], 'name': 'setText', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'contentTypes', 'type': 'uint256'}], 'name': 'ABI', 'outputs': [{'name': 'contentType', 'type': 'uint256'}, {'name': 'data', 'type': 'bytes'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'x', 'type': 'bytes32'}, {'name': 'y', 'type': 'bytes32'}], 'name': 'setPubkey', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'hash', 'type': 'bytes'}], 'name': 'setContenthash', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'addr', 'outputs': [{'name': '', 'type': 'address'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'key', 'type': 'string'}], 'name': 'text', 'outputs': [{'name': '', 'type': 'string'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'contentType', 'type': 'uint256'}, {'name': 'data', 'type': 'bytes'}], 'name': 'setABI', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'name', 'outputs': [{'name': '', 'type': 'string'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'name', 'type': 'string'}], 'name': 'setName', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'contenthash', 'outputs': [{'name': '', 'type': 'bytes'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'pubkey', 'outputs': [{'name': 'x', 'type': 'bytes32'}, {'name': 'y', 'type': 'bytes32'}], 'payable': false, 'stateMutability': 'view', 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'addr', 'type': 'address'}], 'name': 'setAddr', 'outputs': [], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'function'}, {'inputs': [{'name': 'ensAddr', 'type': 'address'}], 'payable': false, 'stateMutability': 'nonpayable', 'type': 'constructor'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'a', 'type': 'address'}], 'name': 'AddrChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'name', 'type': 'string'}], 'name': 'NameChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': true, 'name': 'contentType', 'type': 'uint256'}], 'name': 'ABIChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'x', 'type': 'bytes32'}, {'indexed': false, 'name': 'y', 'type': 'bytes32'}], 'name': 'PubkeyChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'indexedKey', 'type': 'string'}, {'indexed': false, 'name': 'key', 'type': 'string'}], 'name': 'TextChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'hash', 'type': 'bytes'}], 'name': 'ContenthashChanged', 'type': 'event'}] diff --git a/app/scripts/lib/ens-ipfs/resolver.js b/app/scripts/lib/ens-ipfs/resolver.js index b98566190..a0af263bc 100644 --- a/app/scripts/lib/ens-ipfs/resolver.js +++ b/app/scripts/lib/ens-ipfs/resolver.js @@ -1,9 +1,9 @@ const namehash = require('eth-ens-namehash') -const multihash = require('multihashes') const Eth = require('ethjs-query') const EthContract = require('ethjs-contract') -const registrarAbi = require('./contracts/registrar') +const registryAbi = require('./contracts/registry') const resolverAbi = require('./contracts/resolver') +const contentHash = require('content-hash') module.exports = resolveEnsToIpfsContentId @@ -12,37 +12,47 @@ async function resolveEnsToIpfsContentId ({ provider, name }) { const eth = new Eth(provider) const hash = namehash.hash(name) const contract = new EthContract(eth) - // lookup registrar + // lookup registry const chainId = Number.parseInt(await eth.net_version(), 10) - const registrarAddress = getRegistrarForChainId(chainId) - if (!registrarAddress) { - throw new Error(`EnsIpfsResolver - no known ens-ipfs registrar for chainId "${chainId}"`) + const registryAddress = getRegistryForChainId(chainId) + if (!registryAddress) { + throw new Error(`EnsIpfsResolver - no known ens-ipfs registry for chainId "${chainId}"`) } - const Registrar = contract(registrarAbi).at(registrarAddress) + const Registry = contract(registryAbi).at(registryAddress) // lookup resolver - const resolverLookupResult = await Registrar.resolver(hash) + const resolverLookupResult = await Registry.resolver(hash) const resolverAddress = resolverLookupResult[0] if (hexValueIsEmpty(resolverAddress)) { throw new Error(`EnsIpfsResolver - no resolver found for name "${name}"`) } const Resolver = contract(resolverAbi).at(resolverAddress) - // lookup content id - const contentLookupResult = await Resolver.content(hash) - const contentHash = contentLookupResult[0] - if (hexValueIsEmpty(contentHash)) { - throw new Error(`EnsIpfsResolver - no content ID found for name "${name}"`) + + const isEIP1577Compliant = await Resolver.supportsInterface('0xbc1c58d1') + const isLegacyResolver = await Resolver.supportsInterface('0xd8389dc5') + if (isEIP1577Compliant[0]) { + const contentLookupResult = await Resolver.contenthash(hash) + const rawContentHash = contentLookupResult[0] + const decodedContentHash = contentHash.decode(rawContentHash) + const type = contentHash.getCodec(rawContentHash) + return {type: type, hash: decodedContentHash} + } + if (isLegacyResolver[0]) { + // lookup content id + const contentLookupResult = await Resolver.content(hash) + const content = contentLookupResult[0] + if (hexValueIsEmpty(content)) { + throw new Error(`EnsIpfsResolver - no content ID found for name "${name}"`) + } + return {type: 'swarm-ns', hash: content.slice(2)} } - const nonPrefixedHex = contentHash.slice(2) - const buffer = multihash.fromHexString(nonPrefixedHex) - const contentId = multihash.toB58String(multihash.encode(buffer, 'sha2-256')) - return contentId + throw new Error(`EnsIpfsResolver - the resolver for name "${name}" is not standard, it should either supports contenthash() or content()`) } function hexValueIsEmpty (value) { return [undefined, null, '0x', '0x0', '0x0000000000000000000000000000000000000000000000000000000000000000'].includes(value) } -function getRegistrarForChainId (chainId) { +function getRegistryForChainId (chainId) { switch (chainId) { // mainnet case 1: @@ -50,5 +60,11 @@ function getRegistrarForChainId (chainId) { // ropsten case 3: return '0x112234455c3a32fd11230c42e7bccd4a84e02010' + // rinkeby + case 4: + return '0xe7410170f87102df0055eb195163a03b7f2bff4a' + // goerli + case 5: + return '0x112234455c3a32fd11230c42e7bccd4a84e02010' } } diff --git a/app/scripts/lib/ens-ipfs/setup.js b/app/scripts/lib/ens-ipfs/setup.js index df756d0f7..82679db5d 100644 --- a/app/scripts/lib/ens-ipfs/setup.js +++ b/app/scripts/lib/ens-ipfs/setup.js @@ -37,27 +37,25 @@ function setupEnsIpfsResolver ({ provider }) { async function attemptResolve ({ tabId, name, path, search }) { extension.tabs.update(tabId, { url: `loading.html` }) + let url = `https://manager.ens.domains/name/${name}` try { - const ipfsContentId = await resolveEnsToIpfsContentId({ provider, name }) - const url = `https://gateway.ipfs.io/ipfs/${ipfsContentId}${path}${search || ''}` - try { - // check if ipfs gateway has result - const response = await fetch(url, { method: 'HEAD' }) - // if failure, redirect to 404 page - if (response.status !== 200) { - extension.tabs.update(tabId, { url: '404.html' }) - return + const {type, hash} = await resolveEnsToIpfsContentId({ provider, name }) + if (type === 'ipfs-ns') { + const resolvedUrl = `https://gateway.ipfs.io/ipfs/${hash}${path}${search || ''}` + try { + // check if ipfs gateway has result + const response = await fetch(resolvedUrl, { method: 'HEAD' }) + if (response.status === 200) url = resolvedUrl + } catch (err) { + console.warn(err) } - // otherwise redirect to the correct page - extension.tabs.update(tabId, { url }) - } catch (err) { - console.warn(err) - // if HEAD fetch failed, redirect so user can see relevant error page - extension.tabs.update(tabId, { url }) + } else if (type === 'swarm-ns') { + url = `https://swarm-gateways.net/bzz:/${hash}${path}${search || ''}` } } catch (err) { console.warn(err) - extension.tabs.update(tabId, { url: `error.html?name=${name}` }) + } finally { + extension.tabs.update(tabId, { url }) } } } |