b2g/chrome/content/screen.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/b2g/chrome/content/screen.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,258 @@
     1.4 +// screen.js:
     1.5 +// Set the screen size, pixel density and scaling of the b2g client screen
     1.6 +// based on the --screen command-line option, if there is one.
     1.7 +// 
     1.8 +// TODO: support multiple device pixels per CSS pixel
     1.9 +// 
    1.10 +
    1.11 +// We do this on ContentStart because querying the displayDPI fails otherwise.
    1.12 +window.addEventListener('ContentStart', function() {
    1.13 +  // This is the toplevel <window> element
    1.14 +  let shell = document.getElementById('shell');
    1.15 +
    1.16 +  // The <browser> element inside it
    1.17 +  let browser = document.getElementById('systemapp');
    1.18 +
    1.19 +  // Figure out the native resolution of the screen
    1.20 +  let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
    1.21 +    .getInterface(Components.interfaces.nsIDOMWindowUtils);
    1.22 +  let hostDPI = windowUtils.displayDPI;
    1.23 +
    1.24 +  let DEFAULT_SCREEN = '320x480';
    1.25 +
    1.26 +  // This is a somewhat random selection of named screens.
    1.27 +  // Add more to this list when we support more hardware.
    1.28 +  // Data from: http://en.wikipedia.org/wiki/List_of_displays_by_pixel_density
    1.29 +  let screens = {
    1.30 +    iphone: {
    1.31 +      name: 'Apple iPhone', width:320, height:480,  dpi:163
    1.32 +    },
    1.33 +    ipad: {
    1.34 +      name: 'Apple iPad', width:1024, height:768,  dpi:132
    1.35 +    },
    1.36 +    nexus_s: {
    1.37 +      name: 'Samsung Nexus S', width:480, height:800, dpi:235
    1.38 +    },
    1.39 +    galaxy_s2: {
    1.40 +      name: 'Samsung Galaxy SII (I9100)', width:480, height:800, dpi:219
    1.41 +    },
    1.42 +    galaxy_nexus: {
    1.43 +      name: 'Samsung Galaxy Nexus', width:720, height:1280, dpi:316
    1.44 +    },
    1.45 +    galaxy_tab: {
    1.46 +      name: 'Samsung Galaxy Tab 10.1', width:800, height:1280, dpi:149
    1.47 +    },
    1.48 +    wildfire: {
    1.49 +      name: 'HTC Wildfire', width:240, height:320, dpi:125
    1.50 +    },
    1.51 +    tattoo: {
    1.52 +      name: 'HTC Tattoo', width:240, height:320, dpi:143
    1.53 +    },
    1.54 +    salsa: {
    1.55 +      name: 'HTC Salsa', width:320, height:480, dpi:170
    1.56 +    },
    1.57 +    chacha: {
    1.58 +      name: 'HTC ChaCha', width:320, height:480, dpi:222
    1.59 +    },
    1.60 +  };
    1.61 +
    1.62 +  // Get the command line arguments that were passed to the b2g client
    1.63 +  let args;
    1.64 +  try {
    1.65 +    // On Firefox Mulet, we don't always have a command line argument
    1.66 +    args = window.arguments[0].QueryInterface(Ci.nsICommandLine);
    1.67 +  } catch(e) {}
    1.68 +
    1.69 +  let screenarg = null;
    1.70 +
    1.71 +  // Get the --screen argument from the command line
    1.72 +  try {
    1.73 +    if (args) {
    1.74 +      screenarg = args.handleFlagWithParam('screen', false);
    1.75 +    }
    1.76 +
    1.77 +    // If there isn't one, use the default screen
    1.78 +    if (screenarg === null)
    1.79 +      screenarg = DEFAULT_SCREEN;
    1.80 +
    1.81 +    // With no value, tell the user how to use it
    1.82 +    if (screenarg == '')
    1.83 +      usage();
    1.84 +  }
    1.85 +  catch(e) {
    1.86 +    // If getting the argument value fails, its an error
    1.87 +    usage();
    1.88 +  }
    1.89 +  
    1.90 +  // Special case --screen=full goes into fullscreen mode
    1.91 +  if (screenarg === 'full') {
    1.92 +    shell.setAttribute('sizemode', 'fullscreen');
    1.93 +    return;
    1.94 +  } 
    1.95 +
    1.96 +  let rescale = false;
    1.97 +
    1.98 +  // If the value of --screen ends with !, we'll be scaling the output
    1.99 +  if (screenarg[screenarg.length - 1] === '!') {
   1.100 +    rescale = true;
   1.101 +    screenarg = screenarg.substring(0, screenarg.length-1);
   1.102 +  }
   1.103 +
   1.104 +  let width, height, dpi;
   1.105 +
   1.106 +  if (screenarg in screens) {
   1.107 +    // If this is a named screen, get its data
   1.108 +    let screen = screens[screenarg];
   1.109 +    width = screen.width;
   1.110 +    height = screen.height;
   1.111 +    dpi = screen.dpi;
   1.112 +  } else {
   1.113 +    // Otherwise, parse the resolution and density from the --screen value.
   1.114 +    // The supported syntax is WIDTHxHEIGHT[@DPI]
   1.115 +    let match = screenarg.match(/^(\d+)x(\d+)(@(\d+))?$/);
   1.116 +    
   1.117 +    // Display usage information on syntax errors
   1.118 +    if (match == null)
   1.119 +      usage();
   1.120 +    
   1.121 +    // Convert strings to integers
   1.122 +    width = parseInt(match[1], 10);
   1.123 +    height = parseInt(match[2], 10);
   1.124 +    if (match[4])
   1.125 +      dpi = parseInt(match[4], 10);
   1.126 +    else    // If no DPI, use the actual dpi of the host screen
   1.127 +      dpi = hostDPI;
   1.128 +
   1.129 +    // If any of the values came out 0 or NaN or undefined, display usage
   1.130 +    if (!width || !height || !dpi)
   1.131 +      usage();
   1.132 +  }
   1.133 +
   1.134 +  Cu.import("resource://gre/modules/GlobalSimulatorScreen.jsm");
   1.135 +  function resize(width, height, dpi, shouldFlip) {
   1.136 +    GlobalSimulatorScreen.width = width;
   1.137 +    GlobalSimulatorScreen.height = height;
   1.138 +
   1.139 +    // In order to do rescaling, we set the <browser> tag to the specified
   1.140 +    // width and height, and then use a CSS transform to scale it so that
   1.141 +    // it appears at the correct size on the host display.  We also set
   1.142 +    // the size of the <window> element to that scaled target size.
   1.143 +    let scale = rescale ? hostDPI / dpi : 1;
   1.144 +
   1.145 +    // Set the window width and height to desired size plus chrome
   1.146 +    // Include the size of the toolbox displayed under the system app
   1.147 +    let controls = document.getElementById('controls');
   1.148 +    let controlsHeight = 0;
   1.149 +    if (controls) {
   1.150 +      controlsHeight = controls.getBoundingClientRect().height;
   1.151 +    }
   1.152 +    let chromewidth = window.outerWidth - window.innerWidth;
   1.153 +    let chromeheight = window.outerHeight - window.innerHeight + controlsHeight;
   1.154 +    window.resizeTo(Math.round(width * scale) + chromewidth,
   1.155 +                    Math.round(height * scale) + chromeheight);
   1.156 +
   1.157 +    let frameWidth = width, frameHeight = height;
   1.158 +    if (shouldFlip) {
   1.159 +      frameWidth = height;
   1.160 +      frameHeight = width;
   1.161 +    }
   1.162 +
   1.163 +    // Set the browser element to the full unscaled size of the screen
   1.164 +    let style = browser.style;
   1.165 +    style.width = style.minWidth = style.maxWidth =
   1.166 +      frameWidth + 'px';
   1.167 +    style.height = style.minHeight = style.maxHeight =
   1.168 +      frameHeight + 'px';
   1.169 +    browser.setAttribute('flex', '0');  // Don't let it stretch
   1.170 +
   1.171 +    style.transformOrigin = '';
   1.172 +    style.transform = '';
   1.173 +
   1.174 +    // Now scale the browser element as needed
   1.175 +    if (scale !== 1) {
   1.176 +      style.transformOrigin = 'top left';
   1.177 +      style.transform += ' scale(' + scale + ',' + scale + ')';
   1.178 +    }
   1.179 +
   1.180 +    if (shouldFlip) {
   1.181 +      // Display the system app with a 90° clockwise rotation
   1.182 +      let shift = Math.floor(Math.abs(frameWidth-frameHeight) / 2);
   1.183 +      style.transform +=
   1.184 +        ' rotate(0.25turn) translate(-' + shift + 'px, -' + shift + 'px)';
   1.185 +    }
   1.186 +
   1.187 +    // Set the pixel density that we want to simulate.
   1.188 +    // This doesn't change the on-screen size, but makes
   1.189 +    // CSS media queries and mozmm units work right.
   1.190 +    Services.prefs.setIntPref('layout.css.dpi', dpi);
   1.191 +  }
   1.192 +
   1.193 +  // Resize on startup
   1.194 +  resize(width, height, dpi, false);
   1.195 +
   1.196 +  let defaultOrientation = width < height ? 'portrait' : 'landscape';
   1.197 +
   1.198 +  // Then resize on each rotation button click,
   1.199 +  // or when the system app lock/unlock the orientation
   1.200 +  Services.obs.addObserver(function orientationChangeListener(subject) {
   1.201 +    let screen = subject.wrappedJSObject;
   1.202 +    let { mozOrientation, screenOrientation } = screen;
   1.203 +
   1.204 +    let newWidth = width;
   1.205 +    let newHeight = height;
   1.206 +    // If we have an orientation different than the startup one,
   1.207 +    // we switch the sizes
   1.208 +    if (screenOrientation != defaultOrientation) {
   1.209 +      newWidth = height;
   1.210 +      newHeight = width;
   1.211 +    }
   1.212 +
   1.213 +    // If the current app doesn't supports the current screen orientation
   1.214 +    // still resize the window, but rotate its frame so that
   1.215 +    // it is displayed rotated on the side
   1.216 +    let shouldFlip = mozOrientation != screenOrientation;
   1.217 +
   1.218 +    resize(newWidth, newHeight, dpi, shouldFlip);
   1.219 +  }, 'simulator-adjust-window-size', false);
   1.220 +
   1.221 +  // A utility function like console.log() for printing to the terminal window
   1.222 +  // Uses dump(), but enables it first, if necessary
   1.223 +  function print() {
   1.224 +    let dump_enabled =
   1.225 +      Services.prefs.getBoolPref('browser.dom.window.dump.enabled');
   1.226 +
   1.227 +    if (!dump_enabled)
   1.228 +      Services.prefs.setBoolPref('browser.dom.window.dump.enabled', true);
   1.229 +
   1.230 +    dump(Array.prototype.join.call(arguments, ' ') + '\n');
   1.231 +
   1.232 +    if (!dump_enabled) 
   1.233 +      Services.prefs.setBoolPref('browser.dom.window.dump.enabled', false);
   1.234 +  }
   1.235 +
   1.236 +  // Print usage info for --screen and exit
   1.237 +  function usage() {
   1.238 +    // Documentation for the --screen argument
   1.239 +    let msg = 
   1.240 +      'The --screen argument specifies the desired resolution and\n' +
   1.241 +      'pixel density of the simulated device screen. Use it like this:\n' +
   1.242 +      '\t--screen=WIDTHxHEIGHT\t\t\t// E.g.: --screen=320x480\n' +
   1.243 +      '\t--screen=WIDTHxHEIGHT@DOTS_PER_INCH\t// E.g.: --screen=480x800@250\n' +
   1.244 +      '\t--screen=full\t\t\t\t// run in fullscreen mode\n' +
   1.245 +      '\nYou can also specify certain device names:\n';
   1.246 +    for(let p in screens)
   1.247 +      msg += '\t--screen=' + p + '\t// ' + screens[p].name + '\n';
   1.248 +    msg += 
   1.249 +      '\nAdd a ! to the end of a screen specification to rescale the\n' +
   1.250 +      'screen so that it is shown at actual size on your monitor:\n' +
   1.251 +      '\t--screen=nexus_s!\n' +
   1.252 +      '\t--screen=320x480@200!\n'
   1.253 +    ;
   1.254 +
   1.255 +    // Display the usage message
   1.256 +    print(msg);
   1.257 +
   1.258 +    // Exit the b2g client
   1.259 +    Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit);
   1.260 +  }
   1.261 +});

mercurial