diff options
Diffstat (limited to 'chrome_frame/CFInstall.js')
-rw-r--r-- | chrome_frame/CFInstall.js | 232 |
1 files changed, 179 insertions, 53 deletions
diff --git a/chrome_frame/CFInstall.js b/chrome_frame/CFInstall.js index 915f958..b7315bd 100644 --- a/chrome_frame/CFInstall.js +++ b/chrome_frame/CFInstall.js @@ -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 |