Index: chrome_frame/CFInstall.js |
=================================================================== |
--- chrome_frame/CFInstall.js (revision 31504) |
+++ chrome_frame/CFInstall.js (working copy) |
@@ -14,12 +14,12 @@ |
return; |
} |
- /** |
+ /** |
* returns an item based on DOM ID. Optionally a document may be provided to |
* specify the scope to search in. If a node is passed, it's returned as-is. |
* @param {string|Node} id The ID of the node to be located or a node |
* @param {Node} doc Optional A document to search for id. |
- * @return {Node} |
+ * @return {Node} |
*/ |
var byId = function(id, doc) { |
return (typeof id == 'string') ? (doc || document).getElementById(id) : id; |
@@ -28,86 +28,140 @@ |
///////////////////////////////////////////////////////////////////////////// |
// Plugin Detection |
///////////////////////////////////////////////////////////////////////////// |
- |
- var cachedAvailable; |
- /** |
+ /** |
* Checks to find out if ChromeFrame is available as a plugin |
- * @return {Boolean} |
+ * @return {Boolean} |
*/ |
var isAvailable = function() { |
- if (typeof cachedAvailable != 'undefined') { |
- return cachedAvailable; |
+ // For testing purposes. |
+ if (scope.CFInstall._force) { |
+ return scope.CFInstall._forceValue; |
} |
- cachedAvailable = false; |
- |
// Look for CF in the User Agent before trying more expensive checks |
var ua = navigator.userAgent.toLowerCase(); |
- if (ua.indexOf("chromeframe") >= 0 || ua.indexOf("x-clock") >= 0) { |
- cachedAvailable = true; |
- return cachedAvailable; |
+ if (ua.indexOf("chromeframe") >= 0) { |
+ return true; |
} |
if (typeof window['ActiveXObject'] != 'undefined') { |
try { |
var obj = new ActiveXObject('ChromeTab.ChromeFrame'); |
if (obj) { |
- cachedAvailable = true; |
+ return true; |
} |
} catch(e) { |
// squelch |
} |
} |
- return cachedAvailable; |
+ return false; |
}; |
- |
- /** @type {boolean} */ |
- var cfStyleTagInjected = false; |
- |
- /** |
- * Creates a style sheet in the document which provides default styling for |
- * ChromeFrame instances. Successive calls should have no additive effect. |
+ /** |
+ * Creates a style sheet in the document containing the passed rules. |
*/ |
- var injectCFStyleTag = function() { |
- if (cfStyleTagInjected) { |
- // Once and only once |
- return; |
- } |
+ var injectStyleSheet = function(rules) { |
try { |
- var rule = '.chromeFrameInstallDefaultStyle {' + |
- 'width: 500px;' + |
- 'height: 400px;' + |
- 'padding: 0;' + |
- 'border: 1px solid #0028c4;' + |
- 'margin: 0;' + |
- '}'; |
var ss = document.createElement('style'); |
ss.setAttribute('type', 'text/css'); |
if (ss.styleSheet) { |
- ss.styleSheet.cssText = rule; |
+ ss.styleSheet.cssText = rules; |
} else { |
- ss.appendChild(document.createTextNode(rule)); |
+ ss.appendChild(document.createTextNode(rules)); |
} |
var h = document.getElementsByTagName('head')[0]; |
var firstChild = h.firstChild; |
h.insertBefore(ss, firstChild); |
- cfStyleTagInjected = true; |
} catch (e) { |
// squelch |
} |
}; |
+ /** @type {boolean} */ |
+ var cfStyleTagInjected = false; |
+ /** @type {boolean} */ |
+ var cfHiddenInjected = false; |
- /** |
+ /** |
+ * Injects style rules into the document to handle formatting of Chrome Frame |
+ * prompt. Multiple calls have no effect. |
+ */ |
+ var injectCFStyleTag = function() { |
+ if (cfStyleTagInjected) { |
+ // Once and only once |
+ return; |
+ } |
+ var rules = '.chromeFrameInstallDefaultStyle {' + |
+ 'width: 800px;' + |
+ 'height: 600px;' + |
+ 'position: absolute;' + |
+ 'left: 50%;' + |
+ 'top: 50%;' + |
+ 'margin-left: -400px;' + |
+ 'margin-top: -300px;' + |
+ '}' + |
+ '.chromeFrameOverlayContent {' + |
+ 'position: absolute;' + |
+ 'margin-left: -400px;' + |
+ 'margin-top: -300px;' + |
+ 'left: 50%;' + |
+ 'top: 50%;' + |
+ 'border: 1px solid #93B4D9;' + |
+ 'background-color: white;' + |
+ '}' + |
+ '.chromeFrameOverlayContent iframe {' + |
+ 'width: 800px;' + |
+ 'height: 600px;' + |
+ 'border: none;' + |
+ '}' + |
+ '.chromeFrameOverlayCloseBar {' + |
+ 'height: 1em;' + |
+ 'text-align: right;' + |
+ 'background-color: #CADEF4;' + |
+ '}' + |
+ '.chromeFrameOverlayUnderlay {' + |
+ 'position: absolute;' + |
+ 'width: 100%;' + |
+ 'height: 100%;' + |
+ 'background-color: white;' + |
+ 'opacity: 0.5;' + |
+ '-moz-opacity: 0.5;' + |
+ '-webkit-opacity: 0.5;' + |
+ '-ms-filter: ' + |
+ '"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";' + |
+ 'filter: alpha(opacity=50);' + |
+ '}'; |
+ injectStyleSheet(rules); |
+ cfStyleTagInjected = true; |
+ }; |
+ |
+ /** |
+ * Injects style rules to hide the overlay version of the GCF prompt. |
+ * Multiple calls have no effect. |
+ */ |
+ var closeOverlay = function() { |
+ // IE has a limit to the # of <style> tags allowed, so we avoid |
+ // tempting the fates. |
+ if (cfHiddenInjected) { |
+ return; |
+ } |
+ var rules = '.chromeFrameOverlayContent { display: none; }' + |
+ '.chromeFrameOverlayUnderlay { display: none; }'; |
+ injectStyleSheet(rules); |
+ // Hide the dialog for a year (or until cookies are deleted). |
+ var age = 365 * 24 * 60 * 60 * 1000; |
+ document.cookie = "disableGCFCheck=1;path=/;max-age="+age; |
+ cfHiddenInjected = true; |
+ }; |
+ |
+ /** |
* Plucks properties from the passed arguments and sets them on the passed |
* DOM node |
* @param {Node} node The node to set properties on |
* @param {Object} args A map of user-specified properties to set |
*/ |
var setProperties = function(node, args) { |
- injectCFStyleTag(); |
var srcNode = byId(args['node']); |
@@ -118,7 +172,7 @@ |
node.style.cssText = ' ' + cssText; |
var classText = args['className'] || ''; |
- node.className = 'chromeFrameInstallDefaultStyle ' + classText; |
+ node.className = classText; |
// default if the browser doesn't so we don't show sad-tab |
var src = args['src'] || 'about:blank'; |
@@ -130,20 +184,70 @@ |
} |
}; |
- /** |
+ /** |
* Creates an iframe. |
* @param {Object} args A bag of configuration properties, including values |
* like 'node', 'cssText', 'className', 'id', 'src', etc. |
- * @return {Node} |
+ * @return {Node} |
*/ |
var makeIframe = function(args) { |
var el = document.createElement('iframe'); |
+ el.setAttribute('frameborder', '0'); |
+ el.setAttribute('border', '0'); |
setProperties(el, args); |
return el; |
}; |
+ /** |
+ * Adds an unadorned iframe into the page, taking arguments to customize it. |
+ * @param {Object} args A map of user-specified properties to set |
+ */ |
+ var makeInlinePrompt = function(args) { |
+ args.className = 'chromeFrameInstallDefaultStyle ' + |
+ (args.className || ''); |
+ var ifr = makeIframe(args); |
+ // TODO(slightlyoff): handle placement more elegantly! |
+ if (!ifr.parentNode) { |
+ var firstChild = document.body.firstChild; |
+ document.body.insertBefore(ifr, firstChild); |
+ } |
+ }; |
+ |
+ /** |
+ * Adds a styled, closable iframe into the page with a background that |
+ * emulates a modal dialog. |
+ * @param {Object} args A map of user-specified properties to set |
+ */ |
+ var makeOverlayPrompt = function(args) { |
+ if (byId('chromeFrameOverlayContent')) { |
+ return; // Was previously created. Bail. |
+ } |
+ |
+ var n = document.createElement('span'); |
+ n.innerHTML = '<div class="chromeFrameOverlayUnderlay"></div>' + |
+ '<table class="chromeFrameOverlayContent"' + |
+ 'id="chromeFrameOverlayContent"' + |
+ 'cellpadding="0" cellspacing="0">' + |
+ '<tr class="chromeFrameOverlayCloseBar">' + |
+ '<td>' + |
+ // TODO(slightlyoff): i18n |
+ '<button id="chromeFrameCloseButton">close</button>' + |
+ '</td>' + |
+ '</tr>' + |
+ '<tr>' + |
+ '<td id="chromeFrameIframeHolder"></td>' + |
+ '</tr>' + |
+ '</table>'; |
+ |
+ document.body.appendChild(n); |
+ var ifr = makeIframe(args); |
+ byId('chromeFrameIframeHolder').appendChild(ifr); |
+ byId('chromeFrameCloseButton').onclick = closeOverlay; |
+ }; |
+ |
var CFInstall = {}; |
- /** |
+ |
+ /** |
* Checks to see if Chrome Frame is available, if not, prompts the user to |
* install. Once installation is begun, a background timer starts, |
* checkinging for a successful install every 2 seconds. Upon detection of |
@@ -158,17 +262,40 @@ |
CFInstall.check = function(args) { |
args = args || {}; |
- // We currently only support CF in IE |
+ // We currently only support CF in IE |
// TODO(slightlyoff): Update this should we support other browsers! |
- var ieRe = /MSIE (\S+)/; |
- if (!ieRe.test(navigator.userAgent)) { |
+ var ua = navigator.userAgent; |
+ var ieRe = /MSIE \S+; Windows NT/; |
+ var bail = false; |
+ if (ieRe.test(ua)) { |
+ // We also only support Win2003/XPSP2 or better. See: |
+ // http://msdn.microsoft.com/en-us/library/ms537503%28VS.85%29.aspx |
+ if (parseFloat(ua.split(ieRe)[1]) < 6 && |
+ ua.indexOf('SV1') >= 0) { |
+ bail = true; |
+ } |
+ } else { |
+ bail = true; |
+ } |
+ if (bail) { |
return; |
} |
+ // Inject the default styles |
+ injectCFStyleTag(); |
+ if (document.cookie.indexOf("disableGCFCheck=1") >=0) { |
+ // If we're supposed to hide the overlay prompt, add the rules to do it. |
+ closeOverlay(); |
+ } |
+ |
+ // When loaded in an alternate protocol (e.g., "file:"), still call out to |
+ // the right location. |
+ var currentProtocol = document.location.protocol; |
+ var protocol = (currentProtocol == 'https:') ? 'https:' : 'http:'; |
// TODO(slightlyoff): Update this URL when a mini-installer page is |
// available. |
- var installUrl = '//www.google.com/chromeframe'; |
+ var installUrl = protocol + '//www.google.com/chromeframe'; |
if (!isAvailable()) { |
if (args.onmissing) { |
args.onmissing(); |
@@ -180,12 +307,9 @@ |
if (!preventPrompt) { |
if (mode == 'inline') { |
- var ifr = makeIframe(args); |
- // TODO(slightlyoff): handle placement more elegantly! |
- if (!ifr.parentNode) { |
- var firstChild = document.body.firstChild; |
- document.body.insertBefore(ifr, firstChild); |
- } |
+ makeInlinePrompt(args); |
+ } else if (mode == 'overlay') { |
+ makeOverlayPrompt(args); |
} else { |
window.open(args.src); |
} |
@@ -213,6 +337,8 @@ |
} |
}; |
+ CFInstall._force = false; |
+ CFInstall._forceValue = false; |
CFInstall.isAvailable = isAvailable; |
// expose CFInstall to the external scope. We've already checked to make |