diff --git a/CHANGES.md b/CHANGES.md index 1501a4548b4..5296fef8da3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ CKEditor 4 Changelog * [#6504](http://dev.ckeditor.com/ticket/6504): Resolved race condition while loading several customConfig files. * [#10298](http://dev.ckeditor.com/ticket/10298): Data processor breaks attributes containing protected parts. * [#9945](http://dev.ckeditor.com/ticket/9945): [iOS] Scrolling not possible on iPad. +* [#10165](http://dev.ckeditor.com/ticket/10165): [IE] Access denied error if document.domain has been altered. ## CKEditor 4.1.1 diff --git a/core/dom/document.js b/core/dom/document.js index ec7e6d5c1f4..9ba43d927f4 100644 --- a/core/dom/document.js +++ b/core/dom/document.js @@ -261,7 +261,12 @@ CKEDITOR.tools.extend( CKEDITOR.dom.document.prototype, { this.$.open( 'text/html', 'replace' ); // Support for custom document.domain in IE. - CKEDITOR.env.isCustomDomain() && ( this.$.domain = document.domain ); + // + // The script must be appended because if placed before the + // doctype, IE will go into quirks mode and mess with + // the editable, e.g. by changing its default height. + if ( CKEDITOR.env.ie ) + html = html.replace( /(?:^\s*]*?>)|^/i, '$&\n' ); this.$.write( html ); this.$.close(); diff --git a/core/env.js b/core/env.js index b04fcc70280..88de856decd 100644 --- a/core/env.js +++ b/core/env.js @@ -109,6 +109,7 @@ if ( !CKEDITOR.env ) { * alert( 'I\'m in a custom domain!' ); * * @returns {Boolean} `true` if a custom domain is enabled. + * @deprecated */ isCustomDomain: function() { if ( !this.ie ) diff --git a/core/tools.js b/core/tools.js index 7c69e73903f..5e9e0890ef5 100644 --- a/core/tools.js +++ b/core/tools.js @@ -971,6 +971,45 @@ obj[ arr[ i ] ] = fillWith; return obj; + }, + + /** + * Tries to fix the document.domain of the current document to match the + * parent window domain, avoiding "Same Origin" policy issues. + * This is a IE only requirement. + * + * @since 4.1.2 + * @returns {boolean} True if the current domain is already good or if + * it has been fixed successfully. + */ + fixDomain: function() { + var domain; + + while( 1 ) { + try { + // Try to access the parent document. It throws + // "access denied" if restricted by the "Same Origin" policy. + domain = parent.document.domain; + return true; + } catch ( e ) { + // Calculate the value to set to document.domain. + domain = domain ? + + // If it is not the first pass, strip one part of the + // name. E.g. "test.example.com" => "example.com" + domain.replace( /.+?(?:\.|$)/, '' ) : + + // In the first pass, we'll handle the + // "document.domain = document.domain" case. + document.domain; + + // Stop here if there is no more domain parts available. + if ( !domain ) + return false; + + document.domain = domain; + } + } } }; })(); diff --git a/plugins/clipboard/dialogs/paste.js b/plugins/clipboard/dialogs/paste.js index 793f759a962..aeb7b00f208 100644 --- a/plugins/clipboard/dialogs/paste.js +++ b/plugins/clipboard/dialogs/paste.js @@ -5,7 +5,6 @@ CKEDITOR.dialog.add( 'paste', function( editor ) { var lang = editor.lang.clipboard; - var isCustomDomain = CKEDITOR.env.isCustomDomain(); function onPasteFrameLoad( win ) { var doc = new CKEDITOR.dom.document( win.document ), @@ -130,12 +129,12 @@ CKEDITOR.dialog.add( 'paste', function( editor ) { var src = CKEDITOR.env.air ? 'javascript:void(0)' : - isCustomDomain ? - 'javascript:void((function(){' + + CKEDITOR.env.ie ? + 'javascript:void((function(){' + encodeURIComponent( 'document.open();' + - 'document.domain=\'' + document.domain + '\';' + - 'document.close();' + - '})())"' + '(' + CKEDITOR.tools.fixDomain + ')();' + + 'document.close();' + ) + '})())"' : ''; var iframe = CKEDITOR.dom.element.createFromHtml( '' ); focusGrabber.on( 'focus', function() { - iframe.$.contentWindow.focus(); + // Since fixDomain is called in src attribute, + // IE needs some slight delay to correctly move focus. + setTimeout( function() { + iframe.$.contentWindow.focus(); + }); }); container.append( focusGrabber ); diff --git a/plugins/dialog/plugin.js b/plugins/dialog/plugin.js index 9a8e2dd74e1..f08c93c5e53 100644 --- a/plugins/dialog/plugin.js +++ b/plugins/dialog/plugin.js @@ -161,8 +161,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3; // IFrame shim for dialog that masks activeX in IE. (#7619) if ( CKEDITOR.env.ie && !CKEDITOR.env.ie6Compat ) { - var isCustomDomain = CKEDITOR.env.isCustomDomain(), - src = 'javascript:void(function(){' + encodeURIComponent( 'document.open();' + ( isCustomDomain ? ( 'document.domain="' + document.domain + '";' ) : '' ) + 'document.close();' ) + '}())', + var src = 'javascript:void(function(){' + encodeURIComponent( 'document.open();(' + CKEDITOR.tools.fixDomain + ')();document.close();' ) + '}())', iframe = CKEDITOR.dom.element.createFromHtml( ''; + var iframeHtml = ''; html.push( '', - '
', - // Replicate the field label inside of iframe. - '', - '', - '
', - '', - '' ].join( '' ) ); + var inputId = _.frameId + '_input'; + + frameDocument.$.write( [ + '', + '
', + // Replicate the field label inside of iframe. + '', + '', + '
', + '', + '' + ].join( '' ) ); frameDocument.$.close(); diff --git a/plugins/docprops/dialogs/docprops.js b/plugins/docprops/dialogs/docprops.js index d6704bacfd1..7828089f8da 100644 --- a/plugins/docprops/dialogs/docprops.js +++ b/plugins/docprops/dialogs/docprops.js @@ -146,13 +146,12 @@ CKEDITOR.dialog.add( 'docProps', function( editor ) { }; }; var previewSrc = 'javascript:' + - 'void((function(){' + - encodeURIComponent( 'document.open();' + - ( CKEDITOR.env.isCustomDomain() ? 'document.domain=\'' + document.domain + '\';' : '' ) + - 'document.write( \'' + lang.previewHtml + '\' );' + - 'document.close();' - ) + - '})())'; + 'void((function(){' + encodeURIComponent( + 'document.open();' + + ( CKEDITOR.env.ie ? '(' + CKEDITOR.tools.fixDomain + ')();' : '' ) + + 'document.write( \'' + lang.previewHtml + '\' );' + + 'document.close();' + ) + '})())'; return { title: lang.title, diff --git a/plugins/panel/plugin.js b/plugins/panel/plugin.js index dbce1fa92f5..61067b69978 100644 --- a/plugins/panel/plugin.js +++ b/plugins/panel/plugin.js @@ -163,10 +163,22 @@ }; if ( this.isFramed ) { + // With IE, the custom domain has to be taken care at first, + // for other browers, the 'src' attribute should be left empty to + // trigger iframe's 'load' event. + var src = + CKEDITOR.env.air ? 'javascript:void(0)' : + CKEDITOR.env.ie ? 'javascript:void(function(){' + encodeURIComponent( + 'document.open();' + + // In IE, the document domain must be set any time we call document.open(). + '(' + CKEDITOR.tools.fixDomain + ')();' + + 'document.close();' + ) + '}())' : + ''; + data.frame = frameTpl.output({ id: this.id + '_frame', - src: 'javascript:void(document.open(),' + ( CKEDITOR.env.isCustomDomain() ? 'document.domain=\'' + document.domain + '\',' : '' ) - + 'document.close())">' + src: src }); } diff --git a/plugins/preview/plugin.js b/plugins/preview/plugin.js index 9af5c6d3dbb..db577664a99 100644 --- a/plugins/preview/plugin.js +++ b/plugins/preview/plugin.js @@ -17,7 +17,6 @@ var sHTML, config = editor.config, baseTag = config.baseHref ? '' : '', - isCustomDomain = CKEDITOR.env.isCustomDomain(), eventData; if ( config.fullPage ) { @@ -63,15 +62,17 @@ return false; var sOpenUrl = ''; - if ( isCustomDomain ) { + if ( CKEDITOR.env.ie ) { window._cke_htmlToLoad = eventData.dataValue; sOpenUrl = 'javascript:void( (function(){' + 'document.open();' + - 'document.domain="' + document.domain + '";' + + // Support for custom document.domain. + // Strip comments and replace parent with window.opener in the function body. + ( '(' + CKEDITOR.tools.fixDomain + ')();' ).replace( /\/\/.*?\n/g, '' ).replace( /parent\./g, 'window.opener.' ) + 'document.write( window.opener._cke_htmlToLoad );' + 'document.close();' + 'window.opener._cke_htmlToLoad = null;' + - '})() )'; + '})() )'; } // With Firefox only, we need to open a special preview page, so @@ -84,7 +85,7 @@ var oWindow = window.open( sOpenUrl, null, 'toolbar=yes,location=no,status=yes,menubar=yes,scrollbars=yes,resizable=yes,width=' + iWidth + ',height=' + iHeight + ',left=' + iLeft ); - if ( !isCustomDomain && !CKEDITOR.env.gecko ) { + if ( !CKEDITOR.env.ie && !CKEDITOR.env.gecko ) { var doc = oWindow.document; doc.open(); doc.write( eventData.dataValue ); diff --git a/plugins/wysiwygarea/plugin.js b/plugins/wysiwygarea/plugin.js index 6232ef95f51..c6672cfa1ac 100644 --- a/plugins/wysiwygarea/plugin.js +++ b/plugins/wysiwygarea/plugin.js @@ -19,17 +19,9 @@ } editor.addMode( 'wysiwyg', function( callback ) { - var iframe = CKEDITOR.document.createElement( 'iframe' ); - iframe.setStyles({ width: '100%', height: '100%' } ); - iframe.addClass( 'cke_wysiwyg_frame cke_reset' ); - - var contentSpace = editor.ui.space( 'contents' ); - contentSpace.append( iframe ); - var src = 'document.open();' + - // The document domain must be set any time we - // call document.open(). - ( isCustomDomain ? ( 'document.domain="' + document.domain + '";' ) : '' ) + + // In IE, the document domain must be set any time we call document.open(). + ( CKEDITOR.env.ie ? '(' + CKEDITOR.tools.fixDomain + ')();' : '' ) + 'document.close();'; // With IE, the custom domain has to be taken care at first, @@ -39,6 +31,14 @@ : ''; + var iframe = CKEDITOR.dom.element.createFromHtml( '' ); + iframe.setStyles({ width: '100%', height: '100%' } ); + iframe.addClass( 'cke_wysiwyg_frame cke_reset' ); + + var contentSpace = editor.ui.space( 'contents' ); + contentSpace.append( iframe ); + + // Asynchronous iframe loading is only required in IE>8 and Gecko (other reasons probably). // Do not use it on WebKit as it'll break the browser-back navigation. var useOnloadEvent = CKEDITOR.env.ie || CKEDITOR.env.gecko; @@ -63,10 +63,8 @@ }); iframe.setAttributes({ - frameBorder: 0, 'aria-describedby' : labelId, title: frameLabel, - src: src, tabIndex: editor.tabIndex, allowTransparency: 'true' }); @@ -102,9 +100,6 @@ } }); - // Support for custom document.domain in IE. - var isCustomDomain = CKEDITOR.env.isCustomDomain(); - function onDomReady( win ) { var editor = this.editor, doc = win.document, @@ -412,7 +407,6 @@ // is fully editable even before the editing iframe is fully loaded (#4455). var bootstrapCode = '