diff --git a/apps/sharedtest/test/unit/icons_helper_test.js b/apps/sharedtest/test/unit/icons_helper_test.js index c950f9ef24e8..6795a1f44054 100644 --- a/apps/sharedtest/test/unit/icons_helper_test.js +++ b/apps/sharedtest/test/unit/icons_helper_test.js @@ -56,7 +56,7 @@ suite('Icons Helper', () => { suite('IconsHelper.getIcon()', () => { var placeObj, siteObj; - suiteSetup(() => { + setup(() => { placeObj = { icons: { 'http://example.com/metaTagIconUrl': { @@ -77,6 +77,57 @@ suite('Icons Helper', () => { }; }); + test('Prioritise icons from the Web manifest over the rest', done => { + var origin = 'http://origin.com'; + var path = '/test.png'; + siteObj.manifest = { + origin: origin, + icons: { + '32': path + } + }; + IconsHelper.getIcon('http://example.com', 32, placeObj, siteObj) + .then(iconUrl => { + assert.equal((new URL(iconUrl)).pathname, '/webManifestIconUrl'); + done(); + }); + }); + + test('Prioritise Firefox manifest after Web manifest', done => { + var origin = 'http://origin.com'; + var path = '/test.png'; + siteObj.webManifest = null; + siteObj.manifest = { + origin: origin, + icons: { + '32': path + } + }; + IconsHelper.getIcon('http://example.com', 32, placeObj, siteObj) + .then(iconUrl => { + assert.equal((new URL(iconUrl)).href, origin + path); + done(); + }); + }); + + + test('Works with external icons', done => { + var origin = 'http://origin.com'; + var path = 'http://test.com/test.png'; + siteObj.webManifest = null; + siteObj.manifest = { + origin: origin, + icons: { + '32': path + } + }; + IconsHelper.getIcon('http://example.com', 32, placeObj, siteObj) + .then(iconUrl => { + assert.equal((new URL(iconUrl)).href, path); + done(); + }); + }); + test('Prioritise icons from the web manifest over the rest', done => { IconsHelper.getIcon('http://example.com', 32, placeObj, siteObj) .then(iconUrl => { diff --git a/apps/system/js/app_chrome.js b/apps/system/js/app_chrome.js index 6ef0c228b463..1d657824ef6a 100644 --- a/apps/system/js/app_chrome.js +++ b/apps/system/js/app_chrome.js @@ -102,6 +102,8 @@ this.app.element.classList.add('scrollable'); } } + + this.setSiteIcon(); }; AppChrome.prototype.combinedView = function an_combinedView() { diff --git a/apps/system/js/app_window.js b/apps/system/js/app_window.js index 66d6fa2d04bc..a6f0a3ee0b29 100644 --- a/apps/system/js/app_window.js +++ b/apps/system/js/app_window.js @@ -2461,10 +2461,17 @@ var siteObj = {}; if (this.webManifestURL && this.webManifest) { - siteObj = { - webManifestUrl: this.webManifestURL, - webManifest: this.webManifest - }; + siteObj.webManifestUrl = this.webManifestURL; + siteObj.webManifest = this.webManifest; + } + + if (this.manifest && this.manifest.icons) { + //Getting the icons from the FirefoxOS manifest + if (!this.manifest.origin) { + this.manifest.origin = new URL(this.manifestURL).origin; + } + siteObj.manifestUrl = this.manifestURL; + siteObj.manifest = this.manifest; } if (this.webManifestURL && !this.webManifest) { @@ -2476,7 +2483,6 @@ siteObj); }; - /** * Return a promise resolving to an icon blob. * diff --git a/apps/system/test/unit/app_chrome_test.js b/apps/system/test/unit/app_chrome_test.js index c366aff73cb4..667802ca4d10 100644 --- a/apps/system/test/unit/app_chrome_test.js +++ b/apps/system/test/unit/app_chrome_test.js @@ -36,15 +36,17 @@ suite('system/AppChrome', function() { stubById = this.sinon.stub(document, 'getElementById'); stubById.returns(document.createElement('div')); + requireApp('system/js/base_ui.js'); requireApp('system/js/app_chrome.js', function() { + this.sinon.stub(AppChrome.prototype, 'setSiteIcon'); app = new AppWindow(cloneConfig(fakeWebSite)); app.contextmenu = { isShown: function() {return false;} }; chrome = new AppChrome(app); done(); - }); + }.bind(this)); window.SettingsListener = { observe: function() {} }; }); @@ -289,7 +291,7 @@ suite('system/AppChrome', function() { suite('Navigation events', function() { setup(function() { - this.sinon.stub(chrome, 'setSiteIcon'); + chrome.setSiteIcon.reset(); }); test('loadstart', function() { @@ -574,7 +576,6 @@ suite('system/AppChrome', function() { }); test('theme resets on navigation', function() { - this.sinon.stub(chrome, 'setSiteIcon'); chrome.setThemeColor('orange'); chrome.handleEvent({type: 'mozbrowserloadstart'}); chrome.handleEvent({type: 'mozbrowserloadend'}); @@ -755,6 +756,12 @@ suite('system/AppChrome', function() { chrome.reConfig(); assert.isFalse(chrome.app.element.classList.contains('search-app')); }); + + test('sets the site icon', function() { + var app = new AppWindow(fakeSearchApp); + var chrome = new AppChrome(app); + assert.isTrue(chrome.setSiteIcon.called); + }); }); suite('transition events', function() { @@ -797,6 +804,7 @@ suite('system/AppChrome', function() { setup(function() { var app = new AppWindow(cloneConfig(fakeWebSite)); combinedChrome = new AppChrome(app); + combinedChrome.setSiteIcon.restore(); getIconPromise = new MockPromise(); this.sinon.stub(combinedChrome.app, 'getSiteIconUrl') .returns(getIconPromise); diff --git a/apps/system/test/unit/app_window_test.js b/apps/system/test/unit/app_window_test.js index 2cfe91d89d89..245e2f4288bd 100644 --- a/apps/system/test/unit/app_window_test.js +++ b/apps/system/test/unit/app_window_test.js @@ -2555,21 +2555,48 @@ suite('system/AppWindow', function() { blobPromise.mFulfillToValue({ url: dataURI }); }); - test('getSiteIconUrl', function() { - this.sinon.stub(window, 'Promise', MockPromise); - var app1 = new AppWindow(fakeAppConfig1); - app1.webManifestURL = 'https://example.com/webapp.json'; - app1.webManifest = {}; - var blobPromise = new MockPromise(); - var dataURI = 'data:image/png;base64,abc+'; - this.sinon.stub(app1, 'getIconBlob').returns(blobPromise); + suite('getSiteIconUrl', function() { + var app1; + var SIZE = 64; + var origin = 'http://test.com'; - var promisedIcon = app1.getSiteIconUrl(); - promisedIcon.then(function(icon) { - assert.ok(icon && icon.url); - assert.equal(icon.url, dataURI); + setup(function() { + app1 = new AppWindow(fakeAppConfig1); + }); + + + test('getSiteIconUrl', function() { + this.sinon.stub(window, 'Promise', MockPromise); + app1.webManifestURL = 'https://example.com/webapp.json'; + app1.webManifest = {}; + var blobPromise = new MockPromise(); + var dataURI = 'data:image/png;base64,abc+'; + this.sinon.stub(app1, 'getIconBlob').returns(blobPromise); + + var promisedIcon = app1.getSiteIconUrl(); + promisedIcon.then(function(icon) { + assert.ok(icon && icon.url); + assert.equal(icon.url, dataURI); + }); + blobPromise.mFulfillToValue({ url: dataURI }); + }); + + test('getSiteIconUrl uses manifest icons if available', function() { + app1.manifestURL = 'https://example.com/webapp.json'; + app1.manifest = { + origin: origin, + icons: { + '64': '/test.png' + } + }; + + this.sinon.stub(app1, 'getIconBlob', function (url, size, place, site) { + assert.isTrue(size === SIZE); + assert.isTrue(site.manifest === app1.manifest); + }); + + app1.getSiteIconUrl(SIZE); }); - blobPromise.mFulfillToValue({ url: dataURI }); }); test('Change URL at run time', function() { diff --git a/shared/js/icons_helper.js b/shared/js/icons_helper.js index 2a9ad9ef63e6..db18aa8dd806 100644 --- a/shared/js/icons_helper.js +++ b/shared/js/icons_helper.js @@ -47,7 +47,7 @@ iconTargetSize = iconTargetSize * window.devicePixelRatio; - // First look for an icon in the manifest. + // First look for an icon in the Webmanifest. if (siteObj.webManifestUrl && siteObj.webManifest) { iconUrl = getBestIconFromWebManifest(siteObj.webManifest, iconTargetSize); if (DEBUG && iconUrl) { @@ -55,6 +55,15 @@ } } + // Then look for an icon in the Firefox manifest. + if (!iconUrl && siteObj.manifest) { + siteObj.manifest.icons = _convertToWebManifestIcons(siteObj.manifest); + iconUrl = getBestIconFromWebManifest(siteObj.manifest, iconTargetSize); + if (DEBUG && iconUrl) { + console.log('Icon from Firefox App Manifest'); + } + } + // Otherwise, look into the meta tags. if (!iconUrl && placeObj.icons) { iconUrl = getBestIconFromMetaTags(placeObj.icons, iconTargetSize); @@ -140,6 +149,7 @@ if (!iconURL) { iconURL = potentialIcon.src; } + var sizes = Array.from(potentialIcon.sizes); var nearestSize = getNearestSize(sizes, iconSize, bestSize); @@ -152,6 +162,19 @@ return iconURL ? iconURL.href : null; } + function _convertToWebManifestIcons(manifest) { + return Object.keys(manifest.icons).map(function(size) { + var url = manifest.icons[size]; + var sizes = new Set().add(size + 'x' + size); + url = url.indexOf('http') > -1 ? url : manifest.origin + url; + + return { + src: new URL(url), + sizes: sizes + }; + }); + } + // See bug 1041482, we will need to support better // icons for different part of the system application. // A web page have different ways to defining icons