diff --git a/apps/system/style/window.css b/apps/system/style/window.css index 9d2b844f573f..5c80c386d8a3 100644 --- a/apps/system/style/window.css +++ b/apps/system/style/window.css @@ -20,6 +20,10 @@ background-color: transparent; } +.appWindow:not(.homescreen):not(.searchWindow).light { + background-color: #fff; +} + /* mozbrowser iframe layout */ .appWindow iframe { diff --git a/apps/system/test/marionette/browser_site_loading_background_test.js b/apps/system/test/marionette/browser_site_loading_background_test.js new file mode 100644 index 000000000000..48f44a96b9ce --- /dev/null +++ b/apps/system/test/marionette/browser_site_loading_background_test.js @@ -0,0 +1,102 @@ +'use strict'; + +var assert = require('assert'); +var Home = require( + '../../../../apps/verticalhome/test/marionette/lib/home2'); +var Search = require( + '../../../../apps/search/test/marionette/lib/search'); +var Server = require('../../../../shared/test/integration/server'); +var System = require('./lib/system'); +var Rocketbar = require('./lib/rocketbar'); + +marionette('Browser - Site loading background', function() { + + var client = marionette.client({ + prefs: { + 'dom.w3c_touch_events.enabled': 1 + }, + settings: { + 'ftu.manifestURL': null, + 'lockscreen.enabled': false + } + }); + + var home, rocketbar, search, server, system; + + suiteSetup(function(done) { + Server.create(__dirname + '/fixtures/', function(err, _server) { + server = _server; + done(); + }); + }); + + suiteTeardown(function() { + server.stop(); + }); + + setup(function() { + home = new Home(client); + rocketbar = new Rocketbar(client); + search = new Search(client); + system = new System(client); + system.waitForStartup(); + + // Need to wait for the homescreen to be ready as this test takes a + // screenshot. Without the homescreen, we may take a screenshot of the + // system boot screen. + client.apps.launch(Home.URL); + home.waitForLaunch(); + client.switchToFrame(); + + search.removeGeolocationPermission(); + }); + + /** + * Validates the current background color of the current frame. + * Takes a screenshot and parses the pixel data in canvas. + */ + function validateBackgroundColor(r, g, b) { + var screenshot = client.screenshot(); + var pix = client.executeAsyncScript(function(screenshot) { + var img = document.createElement('img'); + img.src = 'data:image/png;base64,' + screenshot; + + img.onload = function() { + var canvas = document.createElement('canvas'); + canvas.width = img.width; + canvas.height = img.height; + var ctx = canvas.getContext('2d'); + + ctx.drawImage(img, 0, 0); + var x = img.width / 2; + var y = img.height / 2; + + var pixelData = ctx.getImageData(x, y, 1, 1); + marionetteScriptFinished(JSON.stringify(pixelData.data)); + }; + + }, [screenshot]); + pix = JSON.parse(pix); + assert.equal(pix[0], r); + assert.equal(pix[1], g); + assert.equal(pix[2], b); + } + + test('validate loading background color', function() { + var url = server.url('darkpage.html'); + server.cork(url); + + // Use the home-screen search box to open up the system browser + rocketbar.homescreenFocus(); + rocketbar.enterText(url + '\uE006'); + + var frame = client.helper.waitForElement( + 'div[transition-state="opened"] iframe[src="' + url + '"]'); + validateBackgroundColor(255, 255, 255); + + server.uncork(url); + client.switchToFrame(frame); + client.helper.waitForElement('body'); + validateBackgroundColor(0, 0, 0); + }); +}); diff --git a/apps/system/test/marionette/fixtures/darkpage.html b/apps/system/test/marionette/fixtures/darkpage.html new file mode 100644 index 000000000000..667b0cca8920 --- /dev/null +++ b/apps/system/test/marionette/fixtures/darkpage.html @@ -0,0 +1,13 @@ + + + + + Dark page + + + +

Mozilla

+ + diff --git a/shared/test/integration/server.js b/shared/test/integration/server.js index bac06825b304..88a55d8391b3 100644 --- a/shared/test/integration/server.js +++ b/shared/test/integration/server.js @@ -21,8 +21,32 @@ Server.prototype = { * Sends signal to stop child process and stop server. */ stop: function() { - this.child.send('stop'); + this.child.send({ + action: 'stop' + }); this.child.kill(); + }, + + /** + * Cork the response body of the given url while allowing headers. + * @param {String} url to cork + */ + cork: function(url) { + this.child.send({ + action: 'cork', + args: url + }); + }, + + /** + * Allow the body to be sent after calling `.cork`. + * @param {String} url to uncork + */ + uncork: function(url) { + this.child.send({ + action: 'uncork', + args: url + }); } }; diff --git a/shared/test/integration/server_child.js b/shared/test/integration/server_child.js index ee5ac76a5cee..773c1e07f960 100644 --- a/shared/test/integration/server_child.js +++ b/shared/test/integration/server_child.js @@ -4,14 +4,30 @@ var http = require('http'); var emptyPort = require('empty-port'); var nodeStatic = require('node-static'); +var EventEmitter = require('events').EventEmitter; + var root = process.argv[2]; -var Server = { +var server; + +function Server() { + EventEmitter.call(this); +} + +Server.prototype = { + __proto__: EventEmitter.prototype, + + /** * Http server running in this process. */ http: null, + /** + * A map of corked URLs. + */ + corkedUrls: {}, + stop: function() { if (this.http) { this.http.kill(); @@ -23,24 +39,49 @@ var Server = { var file = new nodeStatic.Server(root); this.http = http.createServer(function(req, res) { req.addListener('end', function() { + + // Handle corked urls. + var fullUrl = 'http://' + req.headers.host + req.url; + if (server.corkedUrls[fullUrl]) { + server.once('uncorked ' + fullUrl, file.serve.bind(file, req, res)); + return; + } + // hand off request to node-static file.serve(req, res); }).resume(); }).listen(port); + }, + + cork: function(url) { + this.corkedUrls[url] = true; + }, + + uncork: function(url) { + delete this.corkedUrls[url]; + this.emit('uncorked ' + url); } }; +server = new Server(); + // figure out which port we are on emptyPort({}, function(err, port) { - Server.start(port); + server.start(port); process.send(['start', port]); }); // handle process messages process.on('message', function(data) { - switch (data) { + switch (data.action) { + case 'cork': + server.cork(data.args); + break; + case 'uncork': + server.uncork(data.args); + break; case 'stop': - Server.stop(); + server.stop(); break; } });