|
1 // screen.js: |
|
2 // Set the screen size, pixel density and scaling of the b2g client screen |
|
3 // based on the --screen command-line option, if there is one. |
|
4 // |
|
5 // TODO: support multiple device pixels per CSS pixel |
|
6 // |
|
7 |
|
8 // We do this on ContentStart because querying the displayDPI fails otherwise. |
|
9 window.addEventListener('ContentStart', function() { |
|
10 // This is the toplevel <window> element |
|
11 let shell = document.getElementById('shell'); |
|
12 |
|
13 // The <browser> element inside it |
|
14 let browser = document.getElementById('systemapp'); |
|
15 |
|
16 // Figure out the native resolution of the screen |
|
17 let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor) |
|
18 .getInterface(Components.interfaces.nsIDOMWindowUtils); |
|
19 let hostDPI = windowUtils.displayDPI; |
|
20 |
|
21 let DEFAULT_SCREEN = '320x480'; |
|
22 |
|
23 // This is a somewhat random selection of named screens. |
|
24 // Add more to this list when we support more hardware. |
|
25 // Data from: http://en.wikipedia.org/wiki/List_of_displays_by_pixel_density |
|
26 let screens = { |
|
27 iphone: { |
|
28 name: 'Apple iPhone', width:320, height:480, dpi:163 |
|
29 }, |
|
30 ipad: { |
|
31 name: 'Apple iPad', width:1024, height:768, dpi:132 |
|
32 }, |
|
33 nexus_s: { |
|
34 name: 'Samsung Nexus S', width:480, height:800, dpi:235 |
|
35 }, |
|
36 galaxy_s2: { |
|
37 name: 'Samsung Galaxy SII (I9100)', width:480, height:800, dpi:219 |
|
38 }, |
|
39 galaxy_nexus: { |
|
40 name: 'Samsung Galaxy Nexus', width:720, height:1280, dpi:316 |
|
41 }, |
|
42 galaxy_tab: { |
|
43 name: 'Samsung Galaxy Tab 10.1', width:800, height:1280, dpi:149 |
|
44 }, |
|
45 wildfire: { |
|
46 name: 'HTC Wildfire', width:240, height:320, dpi:125 |
|
47 }, |
|
48 tattoo: { |
|
49 name: 'HTC Tattoo', width:240, height:320, dpi:143 |
|
50 }, |
|
51 salsa: { |
|
52 name: 'HTC Salsa', width:320, height:480, dpi:170 |
|
53 }, |
|
54 chacha: { |
|
55 name: 'HTC ChaCha', width:320, height:480, dpi:222 |
|
56 }, |
|
57 }; |
|
58 |
|
59 // Get the command line arguments that were passed to the b2g client |
|
60 let args; |
|
61 try { |
|
62 // On Firefox Mulet, we don't always have a command line argument |
|
63 args = window.arguments[0].QueryInterface(Ci.nsICommandLine); |
|
64 } catch(e) {} |
|
65 |
|
66 let screenarg = null; |
|
67 |
|
68 // Get the --screen argument from the command line |
|
69 try { |
|
70 if (args) { |
|
71 screenarg = args.handleFlagWithParam('screen', false); |
|
72 } |
|
73 |
|
74 // If there isn't one, use the default screen |
|
75 if (screenarg === null) |
|
76 screenarg = DEFAULT_SCREEN; |
|
77 |
|
78 // With no value, tell the user how to use it |
|
79 if (screenarg == '') |
|
80 usage(); |
|
81 } |
|
82 catch(e) { |
|
83 // If getting the argument value fails, its an error |
|
84 usage(); |
|
85 } |
|
86 |
|
87 // Special case --screen=full goes into fullscreen mode |
|
88 if (screenarg === 'full') { |
|
89 shell.setAttribute('sizemode', 'fullscreen'); |
|
90 return; |
|
91 } |
|
92 |
|
93 let rescale = false; |
|
94 |
|
95 // If the value of --screen ends with !, we'll be scaling the output |
|
96 if (screenarg[screenarg.length - 1] === '!') { |
|
97 rescale = true; |
|
98 screenarg = screenarg.substring(0, screenarg.length-1); |
|
99 } |
|
100 |
|
101 let width, height, dpi; |
|
102 |
|
103 if (screenarg in screens) { |
|
104 // If this is a named screen, get its data |
|
105 let screen = screens[screenarg]; |
|
106 width = screen.width; |
|
107 height = screen.height; |
|
108 dpi = screen.dpi; |
|
109 } else { |
|
110 // Otherwise, parse the resolution and density from the --screen value. |
|
111 // The supported syntax is WIDTHxHEIGHT[@DPI] |
|
112 let match = screenarg.match(/^(\d+)x(\d+)(@(\d+))?$/); |
|
113 |
|
114 // Display usage information on syntax errors |
|
115 if (match == null) |
|
116 usage(); |
|
117 |
|
118 // Convert strings to integers |
|
119 width = parseInt(match[1], 10); |
|
120 height = parseInt(match[2], 10); |
|
121 if (match[4]) |
|
122 dpi = parseInt(match[4], 10); |
|
123 else // If no DPI, use the actual dpi of the host screen |
|
124 dpi = hostDPI; |
|
125 |
|
126 // If any of the values came out 0 or NaN or undefined, display usage |
|
127 if (!width || !height || !dpi) |
|
128 usage(); |
|
129 } |
|
130 |
|
131 Cu.import("resource://gre/modules/GlobalSimulatorScreen.jsm"); |
|
132 function resize(width, height, dpi, shouldFlip) { |
|
133 GlobalSimulatorScreen.width = width; |
|
134 GlobalSimulatorScreen.height = height; |
|
135 |
|
136 // In order to do rescaling, we set the <browser> tag to the specified |
|
137 // width and height, and then use a CSS transform to scale it so that |
|
138 // it appears at the correct size on the host display. We also set |
|
139 // the size of the <window> element to that scaled target size. |
|
140 let scale = rescale ? hostDPI / dpi : 1; |
|
141 |
|
142 // Set the window width and height to desired size plus chrome |
|
143 // Include the size of the toolbox displayed under the system app |
|
144 let controls = document.getElementById('controls'); |
|
145 let controlsHeight = 0; |
|
146 if (controls) { |
|
147 controlsHeight = controls.getBoundingClientRect().height; |
|
148 } |
|
149 let chromewidth = window.outerWidth - window.innerWidth; |
|
150 let chromeheight = window.outerHeight - window.innerHeight + controlsHeight; |
|
151 window.resizeTo(Math.round(width * scale) + chromewidth, |
|
152 Math.round(height * scale) + chromeheight); |
|
153 |
|
154 let frameWidth = width, frameHeight = height; |
|
155 if (shouldFlip) { |
|
156 frameWidth = height; |
|
157 frameHeight = width; |
|
158 } |
|
159 |
|
160 // Set the browser element to the full unscaled size of the screen |
|
161 let style = browser.style; |
|
162 style.width = style.minWidth = style.maxWidth = |
|
163 frameWidth + 'px'; |
|
164 style.height = style.minHeight = style.maxHeight = |
|
165 frameHeight + 'px'; |
|
166 browser.setAttribute('flex', '0'); // Don't let it stretch |
|
167 |
|
168 style.transformOrigin = ''; |
|
169 style.transform = ''; |
|
170 |
|
171 // Now scale the browser element as needed |
|
172 if (scale !== 1) { |
|
173 style.transformOrigin = 'top left'; |
|
174 style.transform += ' scale(' + scale + ',' + scale + ')'; |
|
175 } |
|
176 |
|
177 if (shouldFlip) { |
|
178 // Display the system app with a 90° clockwise rotation |
|
179 let shift = Math.floor(Math.abs(frameWidth-frameHeight) / 2); |
|
180 style.transform += |
|
181 ' rotate(0.25turn) translate(-' + shift + 'px, -' + shift + 'px)'; |
|
182 } |
|
183 |
|
184 // Set the pixel density that we want to simulate. |
|
185 // This doesn't change the on-screen size, but makes |
|
186 // CSS media queries and mozmm units work right. |
|
187 Services.prefs.setIntPref('layout.css.dpi', dpi); |
|
188 } |
|
189 |
|
190 // Resize on startup |
|
191 resize(width, height, dpi, false); |
|
192 |
|
193 let defaultOrientation = width < height ? 'portrait' : 'landscape'; |
|
194 |
|
195 // Then resize on each rotation button click, |
|
196 // or when the system app lock/unlock the orientation |
|
197 Services.obs.addObserver(function orientationChangeListener(subject) { |
|
198 let screen = subject.wrappedJSObject; |
|
199 let { mozOrientation, screenOrientation } = screen; |
|
200 |
|
201 let newWidth = width; |
|
202 let newHeight = height; |
|
203 // If we have an orientation different than the startup one, |
|
204 // we switch the sizes |
|
205 if (screenOrientation != defaultOrientation) { |
|
206 newWidth = height; |
|
207 newHeight = width; |
|
208 } |
|
209 |
|
210 // If the current app doesn't supports the current screen orientation |
|
211 // still resize the window, but rotate its frame so that |
|
212 // it is displayed rotated on the side |
|
213 let shouldFlip = mozOrientation != screenOrientation; |
|
214 |
|
215 resize(newWidth, newHeight, dpi, shouldFlip); |
|
216 }, 'simulator-adjust-window-size', false); |
|
217 |
|
218 // A utility function like console.log() for printing to the terminal window |
|
219 // Uses dump(), but enables it first, if necessary |
|
220 function print() { |
|
221 let dump_enabled = |
|
222 Services.prefs.getBoolPref('browser.dom.window.dump.enabled'); |
|
223 |
|
224 if (!dump_enabled) |
|
225 Services.prefs.setBoolPref('browser.dom.window.dump.enabled', true); |
|
226 |
|
227 dump(Array.prototype.join.call(arguments, ' ') + '\n'); |
|
228 |
|
229 if (!dump_enabled) |
|
230 Services.prefs.setBoolPref('browser.dom.window.dump.enabled', false); |
|
231 } |
|
232 |
|
233 // Print usage info for --screen and exit |
|
234 function usage() { |
|
235 // Documentation for the --screen argument |
|
236 let msg = |
|
237 'The --screen argument specifies the desired resolution and\n' + |
|
238 'pixel density of the simulated device screen. Use it like this:\n' + |
|
239 '\t--screen=WIDTHxHEIGHT\t\t\t// E.g.: --screen=320x480\n' + |
|
240 '\t--screen=WIDTHxHEIGHT@DOTS_PER_INCH\t// E.g.: --screen=480x800@250\n' + |
|
241 '\t--screen=full\t\t\t\t// run in fullscreen mode\n' + |
|
242 '\nYou can also specify certain device names:\n'; |
|
243 for(let p in screens) |
|
244 msg += '\t--screen=' + p + '\t// ' + screens[p].name + '\n'; |
|
245 msg += |
|
246 '\nAdd a ! to the end of a screen specification to rescale the\n' + |
|
247 'screen so that it is shown at actual size on your monitor:\n' + |
|
248 '\t--screen=nexus_s!\n' + |
|
249 '\t--screen=320x480@200!\n' |
|
250 ; |
|
251 |
|
252 // Display the usage message |
|
253 print(msg); |
|
254 |
|
255 // Exit the b2g client |
|
256 Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit); |
|
257 } |
|
258 }); |