/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ /* * Tests for replaceFaviconData() */ var iconsvc = PlacesUtils.favicons; var originalFavicon = { file: do_get_file("favicon-normal16.png"), uri: uri(do_get_file("favicon-normal16.png")), data: readFileData(do_get_file("favicon-normal16.png")), mimetype: "image/png", }; var uniqueFaviconId = 0; function createFavicon(fileName) { let tempdir = Services.dirsvc.get("TmpD", Ci.nsIFile); // remove any existing file at the path we're about to copy to let outfile = tempdir.clone(); outfile.append(fileName); try { outfile.remove(false); } catch (e) {} originalFavicon.file.copyToFollowingLinks(tempdir, fileName); let stream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance( Ci.nsIFileOutputStream ); stream.init(outfile, 0x02 | 0x08 | 0x10, 0o600, 0); // append some data that sniffers/encoders will ignore that will distinguish // the different favicons we'll create uniqueFaviconId++; let uniqueStr = "uid:" + uniqueFaviconId; stream.write(uniqueStr, uniqueStr.length); stream.close(); Assert.equal(outfile.leafName.substr(0, fileName.length), fileName); return { file: outfile, uri: uri(outfile), data: readFileData(outfile), mimetype: "image/png", }; } function createDataURLForFavicon(favicon) { return "data:" + favicon.mimetype + ";base64," + toBase64(favicon.data); } function checkCallbackSucceeded( callbackMimetype, callbackData, sourceMimetype, sourceData ) { Assert.equal(callbackMimetype, sourceMimetype); Assert.ok(compareArrays(callbackData, sourceData)); } function run_test() { // check that the favicon loaded correctly Assert.equal(originalFavicon.data.length, 286); run_next_test(); } add_task(async function test_replaceFaviconDataFromDataURL_validHistoryURI() { info("test replaceFaviconDataFromDataURL for valid history uri"); let pageURI = uri("http://test1.bar/"); await PlacesTestUtils.addVisits(pageURI); let favicon = createFavicon("favicon1.png"); iconsvc.replaceFaviconDataFromDataURL( favicon.uri, createDataURLForFavicon(favicon), 0, Services.scriptSecurityManager.getSystemPrincipal() ); await new Promise(resolve => { iconsvc.setAndFetchFaviconForPage( pageURI, favicon.uri, true, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, function test_replaceFaviconDataFromDataURL_validHistoryURI_check( aURI, aDataLen, aData, aMimeType ) { checkCallbackSucceeded( aMimeType, aData, favicon.mimetype, favicon.data ); checkFaviconDataForPage( pageURI, favicon.mimetype, favicon.data, function test_replaceFaviconDataFromDataURL_validHistoryURI_callback() { favicon.file.remove(false); resolve(); } ); }, Services.scriptSecurityManager.getSystemPrincipal() ); }); await PlacesUtils.history.clear(); }); add_task( async function test_replaceFaviconDataFromDataURL_overrideDefaultFavicon() { info( "test replaceFaviconDataFromDataURL to override a later setAndFetchFaviconForPage" ); let pageURI = uri("http://test2.bar/"); await PlacesTestUtils.addVisits(pageURI); let firstFavicon = createFavicon("favicon2.png"); let secondFavicon = createFavicon("favicon3.png"); iconsvc.replaceFaviconDataFromDataURL( firstFavicon.uri, createDataURLForFavicon(secondFavicon), 0, Services.scriptSecurityManager.getSystemPrincipal() ); await new Promise(resolve => { iconsvc.setAndFetchFaviconForPage( pageURI, firstFavicon.uri, true, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, function test_replaceFaviconDataFromDataURL_overrideDefaultFavicon_check( aURI, aDataLen, aData, aMimeType ) { checkCallbackSucceeded( aMimeType, aData, secondFavicon.mimetype, secondFavicon.data ); checkFaviconDataForPage( pageURI, secondFavicon.mimetype, secondFavicon.data, function test_replaceFaviconDataFromDataURL_overrideDefaultFavicon_callback() { firstFavicon.file.remove(false); secondFavicon.file.remove(false); resolve(); } ); }, Services.scriptSecurityManager.getSystemPrincipal() ); }); await PlacesUtils.history.clear(); } ); add_task(async function test_replaceFaviconDataFromDataURL_replaceExisting() { info( "test replaceFaviconDataFromDataURL to override a previous setAndFetchFaviconForPage" ); let pageURI = uri("http://test3.bar"); await PlacesTestUtils.addVisits(pageURI); let firstFavicon = createFavicon("favicon4.png"); let secondFavicon = createFavicon("favicon5.png"); await new Promise(resolve => { iconsvc.setAndFetchFaviconForPage( pageURI, firstFavicon.uri, true, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, function test_replaceFaviconDataFromDataURL_replaceExisting_firstSet_check( aURI, aDataLen, aData, aMimeType ) { checkCallbackSucceeded( aMimeType, aData, firstFavicon.mimetype, firstFavicon.data ); checkFaviconDataForPage( pageURI, firstFavicon.mimetype, firstFavicon.data, function test_replaceFaviconDataFromDataURL_replaceExisting_firstCallback() { iconsvc.replaceFaviconDataFromDataURL( firstFavicon.uri, createDataURLForFavicon(secondFavicon), 0, Services.scriptSecurityManager.getSystemPrincipal() ); checkFaviconDataForPage( pageURI, secondFavicon.mimetype, secondFavicon.data, function test_replaceFaviconDataFromDataURL_replaceExisting_secondCallback() { firstFavicon.file.remove(false); secondFavicon.file.remove(false); resolve(); } ); } ); }, Services.scriptSecurityManager.getSystemPrincipal() ); }); await PlacesUtils.history.clear(); }); add_task(async function test_replaceFaviconDataFromDataURL_unrelatedReplace() { info("test replaceFaviconDataFromDataURL to not make unrelated changes"); let pageURI = uri("http://test4.bar/"); await PlacesTestUtils.addVisits(pageURI); let favicon = createFavicon("favicon6.png"); let unrelatedFavicon = createFavicon("favicon7.png"); iconsvc.replaceFaviconDataFromDataURL( unrelatedFavicon.uri, createDataURLForFavicon(unrelatedFavicon), 0, Services.scriptSecurityManager.getSystemPrincipal() ); await new Promise(resolve => { iconsvc.setAndFetchFaviconForPage( pageURI, favicon.uri, true, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, function test_replaceFaviconDataFromDataURL_unrelatedReplace_check( aURI, aDataLen, aData, aMimeType ) { checkCallbackSucceeded( aMimeType, aData, favicon.mimetype, favicon.data ); checkFaviconDataForPage( pageURI, favicon.mimetype, favicon.data, function test_replaceFaviconDataFromDataURL_unrelatedReplace_callback() { favicon.file.remove(false); unrelatedFavicon.file.remove(false); resolve(); } ); }, Services.scriptSecurityManager.getSystemPrincipal() ); }); await PlacesUtils.history.clear(); }); add_task(async function test_replaceFaviconDataFromDataURL_badInputs() { info("test replaceFaviconDataFromDataURL to throw on bad inputs"); let favicon = createFavicon("favicon8.png"); let ex = null; try { iconsvc.replaceFaviconDataFromDataURL( favicon.uri, "", 0, Services.scriptSecurityManager.getSystemPrincipal() ); } catch (e) { ex = e; } finally { Assert.ok(!!ex); } ex = null; try { iconsvc.replaceFaviconDataFromDataURL( null, createDataURLForFavicon(favicon), 0, Services.scriptSecurityManager.getSystemPrincipal() ); } catch (e) { ex = e; } finally { Assert.ok(!!ex); } favicon.file.remove(false); await PlacesUtils.history.clear(); }); add_task(async function test_replaceFaviconDataFromDataURL_twiceReplace() { info("test replaceFaviconDataFromDataURL on multiple replacements"); let pageURI = uri("http://test5.bar/"); await PlacesTestUtils.addVisits(pageURI); let firstFavicon = createFavicon("favicon9.png"); let secondFavicon = createFavicon("favicon10.png"); iconsvc.replaceFaviconDataFromDataURL( firstFavicon.uri, createDataURLForFavicon(firstFavicon), 0, Services.scriptSecurityManager.getSystemPrincipal() ); iconsvc.replaceFaviconDataFromDataURL( firstFavicon.uri, createDataURLForFavicon(secondFavicon), 0, Services.scriptSecurityManager.getSystemPrincipal() ); await new Promise(resolve => { iconsvc.setAndFetchFaviconForPage( pageURI, firstFavicon.uri, true, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, function test_replaceFaviconDataFromDataURL_twiceReplace_check( aURI, aDataLen, aData, aMimeType ) { checkCallbackSucceeded( aMimeType, aData, secondFavicon.mimetype, secondFavicon.data ); checkFaviconDataForPage( pageURI, secondFavicon.mimetype, secondFavicon.data, function test_replaceFaviconDataFromDataURL_twiceReplace_callback() { firstFavicon.file.remove(false); secondFavicon.file.remove(false); resolve(); } ); }, Services.scriptSecurityManager.getSystemPrincipal() ); }); await PlacesUtils.history.clear(); }); add_task( async function test_replaceFaviconDataFromDataURL_afterRegularAssign() { info("test replaceFaviconDataFromDataURL after replaceFaviconData"); let pageURI = uri("http://test6.bar/"); await PlacesTestUtils.addVisits(pageURI); let firstFavicon = createFavicon("favicon11.png"); let secondFavicon = createFavicon("favicon12.png"); iconsvc.replaceFaviconData( firstFavicon.uri, firstFavicon.data, firstFavicon.mimetype ); iconsvc.replaceFaviconDataFromDataURL( firstFavicon.uri, createDataURLForFavicon(secondFavicon), 0, Services.scriptSecurityManager.getSystemPrincipal() ); await new Promise(resolve => { iconsvc.setAndFetchFaviconForPage( pageURI, firstFavicon.uri, true, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, function test_replaceFaviconDataFromDataURL_afterRegularAssign_check( aURI, aDataLen, aData, aMimeType ) { checkCallbackSucceeded( aMimeType, aData, secondFavicon.mimetype, secondFavicon.data ); checkFaviconDataForPage( pageURI, secondFavicon.mimetype, secondFavicon.data, function test_replaceFaviconDataFromDataURL_afterRegularAssign_callback() { firstFavicon.file.remove(false); secondFavicon.file.remove(false); resolve(); } ); }, Services.scriptSecurityManager.getSystemPrincipal() ); }); await PlacesUtils.history.clear(); } ); add_task( async function test_replaceFaviconDataFromDataURL_beforeRegularAssign() { info("test replaceFaviconDataFromDataURL before replaceFaviconData"); let pageURI = uri("http://test7.bar/"); await PlacesTestUtils.addVisits(pageURI); let firstFavicon = createFavicon("favicon13.png"); let secondFavicon = createFavicon("favicon14.png"); iconsvc.replaceFaviconDataFromDataURL( firstFavicon.uri, createDataURLForFavicon(firstFavicon), 0, Services.scriptSecurityManager.getSystemPrincipal() ); iconsvc.replaceFaviconData( firstFavicon.uri, secondFavicon.data, secondFavicon.mimetype ); await new Promise(resolve => { iconsvc.setAndFetchFaviconForPage( pageURI, firstFavicon.uri, true, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, function test_replaceFaviconDataFromDataURL_beforeRegularAssign_check( aURI, aDataLen, aData, aMimeType ) { checkCallbackSucceeded( aMimeType, aData, secondFavicon.mimetype, secondFavicon.data ); checkFaviconDataForPage( pageURI, secondFavicon.mimetype, secondFavicon.data, function test_replaceFaviconDataFromDataURL_beforeRegularAssign_callback() { firstFavicon.file.remove(false); secondFavicon.file.remove(false); resolve(); } ); }, Services.scriptSecurityManager.getSystemPrincipal() ); }); await PlacesUtils.history.clear(); } ); /* toBase64 copied from image/test/unit/test_encoder_png.js */ /* Convert data (an array of integers) to a Base64 string. */ const toBase64Table = // eslint-disable-next-line no-useless-concat "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789+/"; const base64Pad = "="; function toBase64(data) { let result = ""; let length = data.length; let i; // Convert every three bytes to 4 ascii characters. for (i = 0; i < length - 2; i += 3) { result += toBase64Table[data[i] >> 2]; result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)]; result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)]; result += toBase64Table[data[i + 2] & 0x3f]; } // Convert the remaining 1 or 2 bytes, pad out to 4 characters. if (length % 3) { i = length - (length % 3); result += toBase64Table[data[i] >> 2]; if (length % 3 == 2) { result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)]; result += toBase64Table[(data[i + 1] & 0x0f) << 2]; result += base64Pad; } else { result += toBase64Table[(data[i] & 0x03) << 4]; result += base64Pad + base64Pad; } } return result; }