1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/places/tests/favicons/test_replaceFaviconDataFromDataURL.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,342 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + * http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +/* 1.8 + * Tests for mozIAsyncFavicons::replaceFaviconData() 1.9 + */ 1.10 + 1.11 +let iconsvc = PlacesUtils.favicons; 1.12 +let histsvc = PlacesUtils.history; 1.13 + 1.14 +let originalFavicon = { 1.15 + file: do_get_file("favicon-normal16.png"), 1.16 + uri: uri(do_get_file("favicon-normal16.png")), 1.17 + data: readFileData(do_get_file("favicon-normal16.png")), 1.18 + mimetype: "image/png" 1.19 +}; 1.20 + 1.21 +let uniqueFaviconId = 0; 1.22 +function createFavicon(fileName) { 1.23 + let tempdir = Services.dirsvc.get("TmpD", Ci.nsILocalFile); 1.24 + 1.25 + // remove any existing file at the path we're about to copy to 1.26 + let outfile = tempdir.clone(); 1.27 + outfile.append(fileName); 1.28 + try { outfile.remove(false); } catch (e) {} 1.29 + 1.30 + originalFavicon.file.copyToFollowingLinks(tempdir, fileName); 1.31 + 1.32 + let stream = Cc["@mozilla.org/network/file-output-stream;1"] 1.33 + .createInstance(Ci.nsIFileOutputStream); 1.34 + stream.init(outfile, 0x02 | 0x08 | 0x10, 0600, 0); 1.35 + 1.36 + // append some data that sniffers/encoders will ignore that will distinguish 1.37 + // the different favicons we'll create 1.38 + uniqueFaviconId++; 1.39 + let uniqueStr = "uid:" + uniqueFaviconId; 1.40 + stream.write(uniqueStr, uniqueStr.length); 1.41 + stream.close(); 1.42 + 1.43 + do_check_eq(outfile.leafName.substr(0, fileName.length), fileName); 1.44 + 1.45 + return { 1.46 + file: outfile, 1.47 + uri: uri(outfile), 1.48 + data: readFileData(outfile), 1.49 + mimetype: "image/png" 1.50 + }; 1.51 +} 1.52 + 1.53 +function createDataURLForFavicon(favicon) { 1.54 + return "data:" + favicon.mimetype + ";base64," + toBase64(favicon.data); 1.55 +} 1.56 + 1.57 +function checkCallbackSucceeded(callbackMimetype, callbackData, sourceMimetype, sourceData) { 1.58 + do_check_eq(callbackMimetype, sourceMimetype); 1.59 + do_check_true(compareArrays(callbackData, sourceData)); 1.60 +} 1.61 + 1.62 +function run_test() { 1.63 + // check that the favicon loaded correctly 1.64 + do_check_eq(originalFavicon.data.length, 286); 1.65 + run_next_test(); 1.66 +}; 1.67 + 1.68 +add_task(function test_replaceFaviconDataFromDataURL_validHistoryURI() { 1.69 + do_log_info("test replaceFaviconDataFromDataURL for valid history uri"); 1.70 + 1.71 + let pageURI = uri("http://test1.bar/"); 1.72 + yield promiseAddVisits(pageURI); 1.73 + 1.74 + let favicon = createFavicon("favicon1.png"); 1.75 + iconsvc.replaceFaviconDataFromDataURL(favicon.uri, createDataURLForFavicon(favicon)); 1.76 + 1.77 + let deferSetAndFetchFavicon = Promise.defer(); 1.78 + iconsvc.setAndFetchFaviconForPage(pageURI, favicon.uri, true, 1.79 + PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, 1.80 + function test_replaceFaviconDataFromDataURL_validHistoryURI_check(aURI, aDataLen, aData, aMimeType) { 1.81 + checkCallbackSucceeded(aMimeType, aData, favicon.mimetype, favicon.data); 1.82 + checkFaviconDataForPage( 1.83 + pageURI, favicon.mimetype, favicon.data, 1.84 + function test_replaceFaviconDataFromDataURL_validHistoryURI_callback() { 1.85 + favicon.file.remove(false); 1.86 + deferSetAndFetchFavicon.resolve(); 1.87 + }); 1.88 + }); 1.89 + yield deferSetAndFetchFavicon.promise; 1.90 + 1.91 + yield promiseClearHistory(); 1.92 +}); 1.93 + 1.94 +add_task(function test_replaceFaviconDataFromDataURL_overrideDefaultFavicon() { 1.95 + do_log_info("test replaceFaviconDataFromDataURL to override a later setAndFetchFaviconForPage"); 1.96 + 1.97 + let pageURI = uri("http://test2.bar/"); 1.98 + yield promiseAddVisits(pageURI); 1.99 + 1.100 + let firstFavicon = createFavicon("favicon2.png"); 1.101 + let secondFavicon = createFavicon("favicon3.png"); 1.102 + 1.103 + iconsvc.replaceFaviconDataFromDataURL(firstFavicon.uri, createDataURLForFavicon(secondFavicon)); 1.104 + 1.105 + let deferSetAndFetchFavicon = Promise.defer(); 1.106 + iconsvc.setAndFetchFaviconForPage( 1.107 + pageURI, firstFavicon.uri, true, 1.108 + PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, 1.109 + function test_replaceFaviconDataFromDataURL_overrideDefaultFavicon_check(aURI, aDataLen, aData, aMimeType) { 1.110 + checkCallbackSucceeded(aMimeType, aData, secondFavicon.mimetype, secondFavicon.data); 1.111 + checkFaviconDataForPage( 1.112 + pageURI, secondFavicon.mimetype, secondFavicon.data, 1.113 + function test_replaceFaviconDataFromDataURL_overrideDefaultFavicon_callback() { 1.114 + firstFavicon.file.remove(false); 1.115 + secondFavicon.file.remove(false); 1.116 + deferSetAndFetchFavicon.resolve(); 1.117 + }); 1.118 + }); 1.119 + yield deferSetAndFetchFavicon.promise; 1.120 + 1.121 + yield promiseClearHistory(); 1.122 +}); 1.123 + 1.124 +add_task(function test_replaceFaviconDataFromDataURL_replaceExisting() { 1.125 + do_log_info("test replaceFaviconDataFromDataURL to override a previous setAndFetchFaviconForPage"); 1.126 + 1.127 + let pageURI = uri("http://test3.bar"); 1.128 + yield promiseAddVisits(pageURI); 1.129 + 1.130 + let firstFavicon = createFavicon("favicon4.png"); 1.131 + let secondFavicon = createFavicon("favicon5.png"); 1.132 + 1.133 + let deferSetAndFetchFavicon = Promise.defer(); 1.134 + iconsvc.setAndFetchFaviconForPage( 1.135 + pageURI, firstFavicon.uri, true, 1.136 + PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, 1.137 + function test_replaceFaviconDataFromDataURL_replaceExisting_firstSet_check(aURI, aDataLen, aData, aMimeType) { 1.138 + checkCallbackSucceeded(aMimeType, aData, firstFavicon.mimetype, firstFavicon.data); 1.139 + checkFaviconDataForPage( 1.140 + pageURI, firstFavicon.mimetype, firstFavicon.data, 1.141 + function test_replaceFaviconDataFromDataURL_replaceExisting_firstCallback() { 1.142 + iconsvc.replaceFaviconDataFromDataURL(firstFavicon.uri, createDataURLForFavicon(secondFavicon)); 1.143 + checkFaviconDataForPage( 1.144 + pageURI, secondFavicon.mimetype, secondFavicon.data, 1.145 + function test_replaceFaviconDataFromDataURL_replaceExisting_secondCallback() { 1.146 + firstFavicon.file.remove(false); 1.147 + secondFavicon.file.remove(false); 1.148 + deferSetAndFetchFavicon.resolve(); 1.149 + }); 1.150 + }); 1.151 + }); 1.152 + yield deferSetAndFetchFavicon.promise; 1.153 + 1.154 + yield promiseClearHistory(); 1.155 +}); 1.156 + 1.157 +add_task(function test_replaceFaviconDataFromDataURL_unrelatedReplace() { 1.158 + do_log_info("test replaceFaviconDataFromDataURL to not make unrelated changes"); 1.159 + 1.160 + let pageURI = uri("http://test4.bar/"); 1.161 + yield promiseAddVisits(pageURI); 1.162 + 1.163 + let favicon = createFavicon("favicon6.png"); 1.164 + let unrelatedFavicon = createFavicon("favicon7.png"); 1.165 + 1.166 + iconsvc.replaceFaviconDataFromDataURL(unrelatedFavicon.uri, createDataURLForFavicon(unrelatedFavicon)); 1.167 + 1.168 + let deferSetAndFetchFavicon = Promise.defer(); 1.169 + iconsvc.setAndFetchFaviconForPage( 1.170 + pageURI, favicon.uri, true, 1.171 + PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, 1.172 + function test_replaceFaviconDataFromDataURL_unrelatedReplace_check(aURI, aDataLen, aData, aMimeType) { 1.173 + checkCallbackSucceeded(aMimeType, aData, favicon.mimetype, favicon.data); 1.174 + checkFaviconDataForPage( 1.175 + pageURI, favicon.mimetype, favicon.data, 1.176 + function test_replaceFaviconDataFromDataURL_unrelatedReplace_callback() { 1.177 + favicon.file.remove(false); 1.178 + unrelatedFavicon.file.remove(false); 1.179 + deferSetAndFetchFavicon.resolve(); 1.180 + }); 1.181 + }); 1.182 + yield deferSetAndFetchFavicon.promise; 1.183 + 1.184 + yield promiseClearHistory(); 1.185 +}); 1.186 + 1.187 +add_task(function test_replaceFaviconDataFromDataURL_badInputs() { 1.188 + do_log_info("test replaceFaviconDataFromDataURL to throw on bad inputs"); 1.189 + 1.190 + let favicon = createFavicon("favicon8.png"); 1.191 + 1.192 + let ex = null; 1.193 + try { 1.194 + iconsvc.replaceFaviconDataFromDataURL(favicon.uri, ""); 1.195 + } catch (e) { 1.196 + ex = e; 1.197 + } finally { 1.198 + do_check_true(!!ex); 1.199 + } 1.200 + 1.201 + ex = null; 1.202 + try { 1.203 + iconsvc.replaceFaviconDataFromDataURL(null, createDataURLForFavicon(favicon)); 1.204 + } catch (e) { 1.205 + ex = e; 1.206 + } finally { 1.207 + do_check_true(!!ex); 1.208 + } 1.209 + 1.210 + favicon.file.remove(false); 1.211 + 1.212 + yield promiseClearHistory(); 1.213 +}); 1.214 + 1.215 +add_task(function test_replaceFaviconDataFromDataURL_twiceReplace() { 1.216 + do_log_info("test replaceFaviconDataFromDataURL on multiple replacements"); 1.217 + 1.218 + let pageURI = uri("http://test5.bar/"); 1.219 + yield promiseAddVisits(pageURI); 1.220 + 1.221 + let firstFavicon = createFavicon("favicon9.png"); 1.222 + let secondFavicon = createFavicon("favicon10.png"); 1.223 + 1.224 + iconsvc.replaceFaviconDataFromDataURL(firstFavicon.uri, createDataURLForFavicon(firstFavicon)); 1.225 + iconsvc.replaceFaviconDataFromDataURL(firstFavicon.uri, createDataURLForFavicon(secondFavicon)); 1.226 + 1.227 + let deferSetAndFetchFavicon = Promise.defer(); 1.228 + iconsvc.setAndFetchFaviconForPage( 1.229 + pageURI, firstFavicon.uri, true, 1.230 + PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, 1.231 + function test_replaceFaviconDataFromDataURL_twiceReplace_check(aURI, aDataLen, aData, aMimeType) { 1.232 + checkCallbackSucceeded(aMimeType, aData, secondFavicon.mimetype, secondFavicon.data); 1.233 + checkFaviconDataForPage( 1.234 + pageURI, secondFavicon.mimetype, secondFavicon.data, 1.235 + function test_replaceFaviconDataFromDataURL_twiceReplace_callback() { 1.236 + firstFavicon.file.remove(false); 1.237 + secondFavicon.file.remove(false); 1.238 + deferSetAndFetchFavicon.resolve(); 1.239 + }); 1.240 + }); 1.241 + yield deferSetAndFetchFavicon.promise; 1.242 + 1.243 + yield promiseClearHistory(); 1.244 +}); 1.245 + 1.246 +add_task(function test_replaceFaviconDataFromDataURL_afterRegularAssign() { 1.247 + do_log_info("test replaceFaviconDataFromDataURL after replaceFaviconData"); 1.248 + 1.249 + let pageURI = uri("http://test6.bar/"); 1.250 + yield promiseAddVisits(pageURI); 1.251 + 1.252 + let firstFavicon = createFavicon("favicon11.png"); 1.253 + let secondFavicon = createFavicon("favicon12.png"); 1.254 + 1.255 + iconsvc.replaceFaviconData( 1.256 + firstFavicon.uri, firstFavicon.data, firstFavicon.data.length, 1.257 + firstFavicon.mimetype); 1.258 + iconsvc.replaceFaviconDataFromDataURL(firstFavicon.uri, createDataURLForFavicon(secondFavicon)); 1.259 + 1.260 + let deferSetAndFetchFavicon = Promise.defer(); 1.261 + iconsvc.setAndFetchFaviconForPage( 1.262 + pageURI, firstFavicon.uri, true, 1.263 + PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, 1.264 + function test_replaceFaviconDataFromDataURL_afterRegularAssign_check(aURI, aDataLen, aData, aMimeType) { 1.265 + checkCallbackSucceeded(aMimeType, aData, secondFavicon.mimetype, secondFavicon.data); 1.266 + checkFaviconDataForPage( 1.267 + pageURI, secondFavicon.mimetype, secondFavicon.data, 1.268 + function test_replaceFaviconDataFromDataURL_afterRegularAssign_callback() { 1.269 + firstFavicon.file.remove(false); 1.270 + secondFavicon.file.remove(false); 1.271 + deferSetAndFetchFavicon.resolve(); 1.272 + }); 1.273 + }); 1.274 + yield deferSetAndFetchFavicon.promise; 1.275 + 1.276 + yield promiseClearHistory(); 1.277 +}); 1.278 + 1.279 +add_task(function test_replaceFaviconDataFromDataURL_beforeRegularAssign() { 1.280 + do_log_info("test replaceFaviconDataFromDataURL before replaceFaviconData"); 1.281 + 1.282 + let pageURI = uri("http://test7.bar/"); 1.283 + yield promiseAddVisits(pageURI); 1.284 + 1.285 + let firstFavicon = createFavicon("favicon13.png"); 1.286 + let secondFavicon = createFavicon("favicon14.png"); 1.287 + 1.288 + iconsvc.replaceFaviconDataFromDataURL(firstFavicon.uri, createDataURLForFavicon(firstFavicon)); 1.289 + iconsvc.replaceFaviconData( 1.290 + firstFavicon.uri, secondFavicon.data, secondFavicon.data.length, 1.291 + secondFavicon.mimetype); 1.292 + 1.293 + let deferSetAndFetchFavicon = Promise.defer(); 1.294 + iconsvc.setAndFetchFaviconForPage( 1.295 + pageURI, firstFavicon.uri, true, 1.296 + PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, 1.297 + function test_replaceFaviconDataFromDataURL_beforeRegularAssign_check(aURI, aDataLen, aData, aMimeType) { 1.298 + checkCallbackSucceeded(aMimeType, aData, secondFavicon.mimetype, secondFavicon.data); 1.299 + checkFaviconDataForPage( 1.300 + pageURI, secondFavicon.mimetype, secondFavicon.data, 1.301 + function test_replaceFaviconDataFromDataURL_beforeRegularAssign_callback() { 1.302 + firstFavicon.file.remove(false); 1.303 + secondFavicon.file.remove(false); 1.304 + deferSetAndFetchFavicon.resolve(); 1.305 + }); 1.306 + }); 1.307 + yield deferSetAndFetchFavicon.promise; 1.308 + 1.309 + yield promiseClearHistory(); 1.310 +}); 1.311 + 1.312 +/* toBase64 copied from image/test/unit/test_encoder_png.js */ 1.313 + 1.314 +/* Convert data (an array of integers) to a Base64 string. */ 1.315 +const toBase64Table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' + 1.316 + '0123456789+/'; 1.317 +const base64Pad = '='; 1.318 +function toBase64(data) { 1.319 + let result = ''; 1.320 + let length = data.length; 1.321 + let i; 1.322 + // Convert every three bytes to 4 ascii characters. 1.323 + for (i = 0; i < (length - 2); i += 3) { 1.324 + result += toBase64Table[data[i] >> 2]; 1.325 + result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)]; 1.326 + result += toBase64Table[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)]; 1.327 + result += toBase64Table[data[i+2] & 0x3f]; 1.328 + } 1.329 + 1.330 + // Convert the remaining 1 or 2 bytes, pad out to 4 characters. 1.331 + if (length%3) { 1.332 + i = length - (length%3); 1.333 + result += toBase64Table[data[i] >> 2]; 1.334 + if ((length%3) == 2) { 1.335 + result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)]; 1.336 + result += toBase64Table[(data[i+1] & 0x0f) << 2]; 1.337 + result += base64Pad; 1.338 + } else { 1.339 + result += toBase64Table[(data[i] & 0x03) << 4]; 1.340 + result += base64Pad + base64Pad; 1.341 + } 1.342 + } 1.343 + 1.344 + return result; 1.345 +}