| 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
|
|
|