Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(410)

Side by Side Diff: chrome_frame/CFInstall.js

Issue 345032: Updates CFInstall.js to:... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 11 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | chrome_frame/test/chrome_frame_unittests.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @fileoverview CFInstall.js provides a set of utilities for managing 6 * @fileoverview CFInstall.js provides a set of utilities for managing
7 * the Chrome Frame detection and installation process. 7 * the Chrome Frame detection and installation process.
8 * @author slightlyoff@google.com (Alex Russell) 8 * @author slightlyoff@google.com (Alex Russell)
9 */ 9 */
10 10
11 (function(scope) { 11 (function(scope) {
12 // bail if we'd be over-writing an existing CFInstall object 12 // bail if we'd be over-writing an existing CFInstall object
13 if (scope['CFInstall']) { 13 if (scope['CFInstall']) {
14 return; 14 return;
15 } 15 }
16 16
17 /** 17 /**
18 * returns an item based on DOM ID. Optionally a document may be provided to 18 * returns an item based on DOM ID. Optionally a document may be provided to
19 * specify the scope to search in. If a node is passed, it's returned as-is. 19 * specify the scope to search in. If a node is passed, it's returned as-is.
20 * @param {string|Node} id The ID of the node to be located or a node 20 * @param {string|Node} id The ID of the node to be located or a node
21 * @param {Node} doc Optional A document to search for id. 21 * @param {Node} doc Optional A document to search for id.
22 * @return {Node} 22 * @return {Node}
23 */ 23 */
24 var byId = function(id, doc) { 24 var byId = function(id, doc) {
25 return (typeof id == 'string') ? (doc || document).getElementById(id) : id; 25 return (typeof id == 'string') ? (doc || document).getElementById(id) : id;
26 }; 26 };
27 27
28 ///////////////////////////////////////////////////////////////////////////// 28 /////////////////////////////////////////////////////////////////////////////
29 // Plugin Detection 29 // Plugin Detection
30 ///////////////////////////////////////////////////////////////////////////// 30 /////////////////////////////////////////////////////////////////////////////
31
32 var cachedAvailable;
33 31
34 /** 32 /**
35 * Checks to find out if ChromeFrame is available as a plugin 33 * Checks to find out if ChromeFrame is available as a plugin
36 * @return {Boolean} 34 * @return {Boolean}
37 */ 35 */
38 var isAvailable = function() { 36 var isAvailable = function() {
39 if (typeof cachedAvailable != 'undefined') { 37 // For testing purposes.
40 return cachedAvailable; 38 if (scope.CFInstall._force) {
39 return scope.CFInstall._forceValue;
41 } 40 }
42 41
43 cachedAvailable = false;
44
45 // Look for CF in the User Agent before trying more expensive checks 42 // Look for CF in the User Agent before trying more expensive checks
46 var ua = navigator.userAgent.toLowerCase(); 43 var ua = navigator.userAgent.toLowerCase();
47 if (ua.indexOf("chromeframe") >= 0 || ua.indexOf("x-clock") >= 0) { 44 if (ua.indexOf("chromeframe") >= 0) {
48 cachedAvailable = true; 45 return true;
49 return cachedAvailable;
50 } 46 }
51 47
52 if (typeof window['ActiveXObject'] != 'undefined') { 48 if (typeof window['ActiveXObject'] != 'undefined') {
53 try { 49 try {
54 var obj = new ActiveXObject('ChromeTab.ChromeFrame'); 50 var obj = new ActiveXObject('ChromeTab.ChromeFrame');
55 if (obj) { 51 if (obj) {
56 cachedAvailable = true; 52 return true;
57 } 53 }
58 } catch(e) { 54 } catch(e) {
59 // squelch 55 // squelch
60 } 56 }
61 } 57 }
62 return cachedAvailable; 58 return false;
63 }; 59 };
64 60
61 /**
62 * Creates a style sheet in the document containing the passed rules.
63 */
64 var injectStyleSheet = function(rules) {
65 try {
66 var ss = document.createElement('style');
67 ss.setAttribute('type', 'text/css');
68 if (ss.styleSheet) {
69 ss.styleSheet.cssText = rules;
70 } else {
71 ss.appendChild(document.createTextNode(rules));
72 }
73 var h = document.getElementsByTagName('head')[0];
74 var firstChild = h.firstChild;
75 h.insertBefore(ss, firstChild);
76 } catch (e) {
77 // squelch
78 }
79 };
65 80
66 /** @type {boolean} */ 81 /** @type {boolean} */
67 var cfStyleTagInjected = false; 82 var cfStyleTagInjected = false;
83 /** @type {boolean} */
84 var cfHiddenInjected = false;
68 85
69 /** 86 /**
70 * Creates a style sheet in the document which provides default styling for 87 * Injects style rules into the document to handle formatting of Chrome Frame
71 * ChromeFrame instances. Successive calls should have no additive effect. 88 * prompt. Multiple calls have no effect.
72 */ 89 */
73 var injectCFStyleTag = function() { 90 var injectCFStyleTag = function() {
74 if (cfStyleTagInjected) { 91 if (cfStyleTagInjected) {
75 // Once and only once 92 // Once and only once
76 return; 93 return;
77 } 94 }
78 try { 95 var rules = '.chromeFrameInstallDefaultStyle {' +
79 var rule = '.chromeFrameInstallDefaultStyle {' + 96 'width: 800px;' +
80 'width: 500px;' + 97 'height: 600px;' +
81 'height: 400px;' + 98 'position: absolute;' +
82 'padding: 0;' + 99 'left: 50%;' +
83 'border: 1px solid #0028c4;' + 100 'top: 50%;' +
84 'margin: 0;' + 101 'margin-left: -400px;' +
85 '}'; 102 'margin-top: -300px;' +
86 var ss = document.createElement('style'); 103 '}' +
87 ss.setAttribute('type', 'text/css'); 104 '.chromeFrameOverlayContent {' +
88 if (ss.styleSheet) { 105 'position: absolute;' +
89 ss.styleSheet.cssText = rule; 106 'margin-left: -400px;' +
90 } else { 107 'margin-top: -300px;' +
91 ss.appendChild(document.createTextNode(rule)); 108 'left: 50%;' +
92 } 109 'top: 50%;' +
93 var h = document.getElementsByTagName('head')[0]; 110 'border: 1px solid #93B4D9;' +
94 var firstChild = h.firstChild; 111 'background-color: white;' +
95 h.insertBefore(ss, firstChild); 112 '}' +
96 cfStyleTagInjected = true; 113 '.chromeFrameOverlayContent iframe {' +
97 } catch (e) { 114 'width: 800px;' +
98 // squelch 115 'height: 600px;' +
99 } 116 'border: none;' +
117 '}' +
118 '.chromeFrameOverlayCloseBar {' +
119 'height: 1em;' +
120 'text-align: right;' +
121 'background-color: #CADEF4;' +
122 '}' +
123 '.chromeFrameOverlayUnderlay {' +
124 'position: absolute;' +
125 'width: 100%;' +
126 'height: 100%;' +
127 'background-color: white;' +
128 'opacity: 0.5;' +
129 '-moz-opacity: 0.5;' +
130 '-webkit-opacity: 0.5;' +
131 '-ms-filter: ' +
132 '"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";' +
133 'filter: alpha(opacity=50);' +
134 '}';
135 injectStyleSheet(rules);
136 cfStyleTagInjected = true;
100 }; 137 };
101 138
139 /**
140 * Injects style rules to hide the overlay version of the GCF prompt.
141 * Multiple calls have no effect.
142 */
143 var closeOverlay = function() {
144 // IE has a limit to the # of <style> tags allowed, so we avoid
145 // tempting the fates.
146 if (cfHiddenInjected) {
147 return;
148 }
149 var rules = '.chromeFrameOverlayContent { display: none; }' +
150 '.chromeFrameOverlayUnderlay { display: none; }';
151 injectStyleSheet(rules);
152 // Hide the dialog for a year (or until cookies are deleted).
153 var age = 365 * 24 * 60 * 60 * 1000;
154 document.cookie = "disableGCFCheck=1;path=/;max-age="+age;
155 cfHiddenInjected = true;
156 };
102 157
103 /** 158 /**
104 * Plucks properties from the passed arguments and sets them on the passed 159 * Plucks properties from the passed arguments and sets them on the passed
105 * DOM node 160 * DOM node
106 * @param {Node} node The node to set properties on 161 * @param {Node} node The node to set properties on
107 * @param {Object} args A map of user-specified properties to set 162 * @param {Object} args A map of user-specified properties to set
108 */ 163 */
109 var setProperties = function(node, args) { 164 var setProperties = function(node, args) {
110 injectCFStyleTag();
111 165
112 var srcNode = byId(args['node']); 166 var srcNode = byId(args['node']);
113 167
114 node.id = args['id'] || (srcNode ? srcNode['id'] || getUid(srcNode) : ''); 168 node.id = args['id'] || (srcNode ? srcNode['id'] || getUid(srcNode) : '');
115 169
116 // TODO(slightlyoff): Opera compat? need to test there 170 // TODO(slightlyoff): Opera compat? need to test there
117 var cssText = args['cssText'] || ''; 171 var cssText = args['cssText'] || '';
118 node.style.cssText = ' ' + cssText; 172 node.style.cssText = ' ' + cssText;
119 173
120 var classText = args['className'] || ''; 174 var classText = args['className'] || '';
121 node.className = 'chromeFrameInstallDefaultStyle ' + classText; 175 node.className = classText;
122 176
123 // default if the browser doesn't so we don't show sad-tab 177 // default if the browser doesn't so we don't show sad-tab
124 var src = args['src'] || 'about:blank'; 178 var src = args['src'] || 'about:blank';
125 179
126 node.src = src; 180 node.src = src;
127 181
128 if (srcNode) { 182 if (srcNode) {
129 srcNode.parentNode.replaceChild(node, srcNode); 183 srcNode.parentNode.replaceChild(node, srcNode);
130 } 184 }
131 }; 185 };
132 186
133 /** 187 /**
134 * Creates an iframe. 188 * Creates an iframe.
135 * @param {Object} args A bag of configuration properties, including values 189 * @param {Object} args A bag of configuration properties, including values
136 * like 'node', 'cssText', 'className', 'id', 'src', etc. 190 * like 'node', 'cssText', 'className', 'id', 'src', etc.
137 * @return {Node} 191 * @return {Node}
138 */ 192 */
139 var makeIframe = function(args) { 193 var makeIframe = function(args) {
140 var el = document.createElement('iframe'); 194 var el = document.createElement('iframe');
195 el.setAttribute('frameborder', '0');
196 el.setAttribute('border', '0');
141 setProperties(el, args); 197 setProperties(el, args);
142 return el; 198 return el;
143 }; 199 };
144 200
201 /**
202 * Adds an unadorned iframe into the page, taking arguments to customize it.
203 * @param {Object} args A map of user-specified properties to set
204 */
205 var makeInlinePrompt = function(args) {
206 args.className = 'chromeFrameInstallDefaultStyle ' +
207 (args.className || '');
208 var ifr = makeIframe(args);
209 // TODO(slightlyoff): handle placement more elegantly!
210 if (!ifr.parentNode) {
211 var firstChild = document.body.firstChild;
212 document.body.insertBefore(ifr, firstChild);
213 }
214 };
215
216 /**
217 * Adds a styled, closable iframe into the page with a background that
218 * emulates a modal dialog.
219 * @param {Object} args A map of user-specified properties to set
220 */
221 var makeOverlayPrompt = function(args) {
222 if (byId('chromeFrameOverlayContent')) {
223 return; // Was previously created. Bail.
224 }
225
226 var n = document.createElement('span');
227 n.innerHTML = '<div class="chromeFrameOverlayUnderlay"></div>' +
228 '<table class="chromeFrameOverlayContent"' +
229 'id="chromeFrameOverlayContent"' +
230 'cellpadding="0" cellspacing="0">' +
231 '<tr class="chromeFrameOverlayCloseBar">' +
232 '<td>' +
233 // TODO(slightlyoff): i18n
234 '<button id="chromeFrameCloseButton">close</button>' +
235 '</td>' +
236 '</tr>' +
237 '<tr>' +
238 '<td id="chromeFrameIframeHolder"></td>' +
239 '</tr>' +
240 '</table>';
241
242 document.body.appendChild(n);
243 var ifr = makeIframe(args);
244 byId('chromeFrameIframeHolder').appendChild(ifr);
245 byId('chromeFrameCloseButton').onclick = closeOverlay;
246 };
247
145 var CFInstall = {}; 248 var CFInstall = {};
146 /** 249
250 /**
147 * Checks to see if Chrome Frame is available, if not, prompts the user to 251 * Checks to see if Chrome Frame is available, if not, prompts the user to
148 * install. Once installation is begun, a background timer starts, 252 * install. Once installation is begun, a background timer starts,
149 * checkinging for a successful install every 2 seconds. Upon detection of 253 * checkinging for a successful install every 2 seconds. Upon detection of
150 * successful installation, the current page is reloaded, or if a 254 * successful installation, the current page is reloaded, or if a
151 * 'destination' parameter is passed, the page navigates there instead. 255 * 'destination' parameter is passed, the page navigates there instead.
152 * @param {Object} args A bag of configuration properties. Respected 256 * @param {Object} args A bag of configuration properties. Respected
153 * properties are: 'mode', 'url', 'destination', 'node', 'onmissing', 257 * properties are: 'mode', 'url', 'destination', 'node', 'onmissing',
154 * 'preventPrompt', 'oninstall', 'preventInstallDetection', 'cssText', and 258 * 'preventPrompt', 'oninstall', 'preventInstallDetection', 'cssText', and
155 * 'className'. 259 * 'className'.
156 * @public 260 * @public
157 */ 261 */
158 CFInstall.check = function(args) { 262 CFInstall.check = function(args) {
159 args = args || {}; 263 args = args || {};
160 264
161 // We currently only support CF in IE 265 // We currently only support CF in IE
162 // TODO(slightlyoff): Update this should we support other browsers! 266 // TODO(slightlyoff): Update this should we support other browsers!
163 var ieRe = /MSIE (\S+)/; 267 var ua = navigator.userAgent;
164 if (!ieRe.test(navigator.userAgent)) { 268 var ieRe = /MSIE \S+; Windows NT/;
269 var bail = false;
270 if (ieRe.test(ua)) {
271 // We also only support Win2003/XPSP2 or better. See:
272 // http://msdn.microsoft.com/en-us/library/ms537503%28VS.85%29.aspx
273 if (parseFloat(ua.split(ieRe)[1]) < 6 &&
274 ua.indexOf('SV1') >= 0) {
275 bail = true;
276 }
277 } else {
278 bail = true;
279 }
280 if (bail) {
165 return; 281 return;
166 } 282 }
167 283
284 // Inject the default styles
285 injectCFStyleTag();
168 286
287 if (document.cookie.indexOf("disableGCFCheck=1") >=0) {
288 // If we're supposed to hide the overlay prompt, add the rules to do it.
289 closeOverlay();
290 }
291
292 // When loaded in an alternate protocol (e.g., "file:"), still call out to
293 // the right location.
294 var currentProtocol = document.location.protocol;
295 var protocol = (currentProtocol == 'https:') ? 'https:' : 'http:';
169 // TODO(slightlyoff): Update this URL when a mini-installer page is 296 // TODO(slightlyoff): Update this URL when a mini-installer page is
170 // available. 297 // available.
171 var installUrl = '//www.google.com/chromeframe'; 298 var installUrl = protocol + '//www.google.com/chromeframe';
172 if (!isAvailable()) { 299 if (!isAvailable()) {
173 if (args.onmissing) { 300 if (args.onmissing) {
174 args.onmissing(); 301 args.onmissing();
175 } 302 }
176 303
177 args.src = args.url || installUrl; 304 args.src = args.url || installUrl;
178 var mode = args.mode || 'inline'; 305 var mode = args.mode || 'inline';
179 var preventPrompt = args.preventPrompt || false; 306 var preventPrompt = args.preventPrompt || false;
180 307
181 if (!preventPrompt) { 308 if (!preventPrompt) {
182 if (mode == 'inline') { 309 if (mode == 'inline') {
183 var ifr = makeIframe(args); 310 makeInlinePrompt(args);
184 // TODO(slightlyoff): handle placement more elegantly! 311 } else if (mode == 'overlay') {
185 if (!ifr.parentNode) { 312 makeOverlayPrompt(args);
186 var firstChild = document.body.firstChild;
187 document.body.insertBefore(ifr, firstChild);
188 }
189 } else { 313 } else {
190 window.open(args.src); 314 window.open(args.src);
191 } 315 }
192 } 316 }
193 317
194 if (args.preventInstallDetection) { 318 if (args.preventInstallDetection) {
195 return; 319 return;
196 } 320 }
197 321
198 // Begin polling for install success. 322 // Begin polling for install success.
199 var installTimer = setInterval(function() { 323 var installTimer = setInterval(function() {
200 // every 2 seconds, look to see if CF is available, if so, proceed on 324 // every 2 seconds, look to see if CF is available, if so, proceed on
201 // to our destination 325 // to our destination
202 if (isAvailable()) { 326 if (isAvailable()) {
203 if (args.oninstall) { 327 if (args.oninstall) {
204 args.oninstall(); 328 args.oninstall();
205 } 329 }
206 330
207 clearInterval(installTimer); 331 clearInterval(installTimer);
208 // TODO(slightlyoff): add a way to prevent navigation or make it 332 // TODO(slightlyoff): add a way to prevent navigation or make it
209 // contingent on oninstall? 333 // contingent on oninstall?
210 window.location = args.destination || window.location; 334 window.location = args.destination || window.location;
211 } 335 }
212 }, 2000); 336 }, 2000);
213 } 337 }
214 }; 338 };
215 339
340 CFInstall._force = false;
341 CFInstall._forceValue = false;
216 CFInstall.isAvailable = isAvailable; 342 CFInstall.isAvailable = isAvailable;
217 343
218 // expose CFInstall to the external scope. We've already checked to make 344 // expose CFInstall to the external scope. We've already checked to make
219 // sure we're not going to blow existing objects away. 345 // sure we're not going to blow existing objects away.
220 scope.CFInstall = CFInstall; 346 scope.CFInstall = CFInstall;
221 347
222 })(this['ChromeFrameInstallScope'] || this); 348 })(this['ChromeFrameInstallScope'] || this);
OLDNEW
« no previous file with comments | « no previous file | chrome_frame/test/chrome_frame_unittests.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698