mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-25 14:56:20 +00:00
e9ca44c86c
We previously manually loaded CSS into these frames, which is flawed because it completely bypasses ResourceLoader (so CSSJanus didn't flip them, necessitating a bunch of hacks for RTL), and doesn't pull in MediaWiki styles (so templates inside references don't render correctly). Instead, this commit copies all styles from the main document into each frame's document, inlining what it can. Loading all styles in dialogs and inspectors caused some problems, initially. We didn't namespace our styles for dialogs vs. inspectors at all; the only reason inspector styles weren't being applied to dialogs and vice versa was because we controlled which files were being loaded in which context. This commit namespaces the inspector and dialog styles where needed so they don't conflict and try to override each other. Tested in Vector and Monobook, but not in Apex and not in RTL. ve.init.mw.ViewPageTarget*.css: * Namespace styles that are only intended for the main document * Undo Monobook's font-size: x-small; in frames *Dialog.js: * Remove addLocalStylesheet() calls, we don't need those any more ** ve.ui.MWDialog seems to be unneeded now, we may want to remove it *.css: * Remove @noflip-ped RTL rules where they were just flipped versions of their LTR counterparts ve.ui.Dialog.css, ve.ui.Inspector.css: * Namespace styles with .ve-ui-dialog-content / .ve-ui-inspector-content ve.ui.Frame.css: * Move the margin:0 and padding:0 here (were in the frame <body>'s style attribute) and add background:none to prevent frames from getting the skin's background (grey in Vector, a book in Monobook) ve.ui.Dialog.js, ve.ui.Inspector.js: * Add ve-ui-dialog-content / ve-ui-inspector-content class to the frame's content <div> so we can restrict styles to only apply in dialogs / inspectors ve.ui.Frame.js: * Replace infrastructure for @import-ing stylesheets with transplantation * Remove code polling to see when the stylesheets were loaded ** We can't do this in the new approach AFAIK, since all styles in the frame are either inlined or inaccessible due to the same-origin policy ** We also shouldn't need it because the browser should have cached the styles when it loaded the main document * Apply ve-ui-frame-body class to the frame's <body> so we can style it ** Move inline padding:0;margin:0; into ve.ui.Frame.css ** Move the ve-ltr/ve-rtl class up to the <body> ve.ui.Window.js: * Remove infrastructure registering stylesheet URLs to load Change-Id: I4a37115301811ad860f4578344a04873ea8c2b69
165 lines
4.5 KiB
JavaScript
165 lines
4.5 KiB
JavaScript
/*!
|
|
* VisualEditor UserInterface Frame class.
|
|
*
|
|
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
|
* @license The MIT License (MIT); see LICENSE.txt
|
|
*/
|
|
|
|
/**
|
|
* UserInterface iframe abstraction.
|
|
*
|
|
* @class
|
|
* @extends ve.Element
|
|
* @mixins ve.EventEmitter
|
|
*
|
|
* @constructor
|
|
* @param {Object} [config] Config options
|
|
*/
|
|
ve.ui.Frame = function VeUiFrame( config ) {
|
|
// Parent constructor
|
|
ve.Element.call( this, config );
|
|
|
|
// Mixin constructors
|
|
ve.EventEmitter.call( this );
|
|
|
|
// Properties
|
|
this.initialized = false;
|
|
this.config = config;
|
|
|
|
// Initialize
|
|
this.$
|
|
.addClass( 've-ui-frame' )
|
|
.attr( { 'frameborder': 0, 'scrolling': 'no' } );
|
|
|
|
};
|
|
|
|
/* Inheritance */
|
|
|
|
ve.inheritClass( ve.ui.Frame, ve.Element );
|
|
|
|
ve.mixinClass( ve.ui.Frame, ve.EventEmitter );
|
|
|
|
/* Static Properties */
|
|
|
|
ve.ui.Frame.static.tagName = 'iframe';
|
|
|
|
/* Events */
|
|
|
|
/**
|
|
* @event initialize
|
|
*/
|
|
|
|
/* Methods */
|
|
|
|
/**
|
|
* Load the frame contents.
|
|
*
|
|
* Once the iframe's stylesheets are loaded, the `initialize` event will be emitted.
|
|
*
|
|
* Sounds simple right? Read on...
|
|
*
|
|
* When you create a dynamic iframe using open/write/close, the window.load event for the
|
|
* iframe is triggered when you call close, and there's no further load event to indicate that
|
|
* everything is actually loaded.
|
|
*
|
|
* By dynamically adding stylesheet links, we can detect when each link is loaded by testing if we
|
|
* have access to each of their `sheet.cssRules` properties. Every 10ms we poll to see if we have
|
|
* access to the style's `sheet.cssRules` property yet.
|
|
*
|
|
* However, because of security issues, we never have such access if the stylesheet came from a
|
|
* different site. Thus, we are left with linking to the stylesheets through a style element with
|
|
* multiple `@import` statements - which ends up being simpler anyway. Since we created that style,
|
|
* we always have access, and its contents are only available when everything is done loading.
|
|
*
|
|
* @emits initialize
|
|
*/
|
|
ve.ui.Frame.prototype.load = function () {
|
|
var win = this.$.prop( 'contentWindow' ),
|
|
doc = win.document;
|
|
|
|
// Figure out directionality:
|
|
this.dir = this.$.closest( '[dir]' ).prop( 'dir' ) || 'ltr';
|
|
|
|
// Initialize contents
|
|
doc.open();
|
|
doc.write(
|
|
'<!doctype html>' +
|
|
'<html>' +
|
|
'<body class="ve-ui-frame-body ve-' + this.dir + '" style="direction:' + this.dir + ';" dir="' + this.dir + '">' +
|
|
'<div class="ve-ui-frame-content"></div>' +
|
|
'</body>' +
|
|
'</html>'
|
|
);
|
|
doc.close();
|
|
|
|
// Properties
|
|
this.$$ = ve.Element.get$$( doc, this );
|
|
this.$content = this.$$( '.ve-ui-frame-content' );
|
|
this.$document = this.$$( doc );
|
|
|
|
this.transplantStyles();
|
|
this.initialized = true;
|
|
this.emit( 'initialize' );
|
|
};
|
|
|
|
/**
|
|
* Transplant the CSS styles from the frame's parent document to the frame's document.
|
|
*
|
|
* This loops over the style sheets in the parent document, and copies their tags to the
|
|
* frame's document. `<link>` tags pointing to same-origin style sheets are inlined as `<style>` tags;
|
|
* `<link>` tags pointing to foreign URLs and `<style>` tags are copied verbatim.
|
|
*/
|
|
ve.ui.Frame.prototype.transplantStyles = function () {
|
|
var i, ilen, j, jlen, sheet, rules, cssText, styleNode,
|
|
newDoc = this.$document[0],
|
|
parentDoc = this.getElementDocument();
|
|
for ( i = 0, ilen = parentDoc.styleSheets.length; i < ilen; i++ ) {
|
|
sheet = parentDoc.styleSheets[i];
|
|
try {
|
|
rules = sheet.cssRules;
|
|
} catch ( e ) { }
|
|
if ( sheet.ownerNode.nodeName.toLowerCase() === 'link' && rules ) {
|
|
// This is a <link> tag pointing to a same-origin style sheet. Rebuild it as a
|
|
// <style> tag
|
|
cssText = '';
|
|
for ( j = 0, jlen = sheet.cssRules.length; j < jlen; j++ ) {
|
|
cssText += sheet.cssRules[j].cssText + '\n';
|
|
}
|
|
cssText += '/* Transplanted styles from ' + sheet.href + ' */\n';
|
|
styleNode = newDoc.createElement( 'style' );
|
|
styleNode.textContent = cssText;
|
|
} else {
|
|
// It's either a <style> tag or a <link> tag pointing to a foreign URL; just copy
|
|
// it to the new document
|
|
styleNode = newDoc.importNode( sheet.ownerNode, true );
|
|
}
|
|
newDoc.body.appendChild( styleNode );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Run a callback as soon as the frame has been initialized.
|
|
*
|
|
* @param {Function} callback
|
|
*/
|
|
ve.ui.Frame.prototype.run = function ( callback ) {
|
|
if ( this.initialized ) {
|
|
callback();
|
|
} else {
|
|
this.once( 'initialize', callback );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Sets the size of the frame.
|
|
*
|
|
* @method
|
|
* @param {number} width Frame width in pixels
|
|
* @param {number} height Frame height in pixels
|
|
* @chainable
|
|
*/
|
|
ve.ui.Frame.prototype.setSize = function ( width, height ) {
|
|
this.$.css( { 'width': width, 'height': height } );
|
|
return this;
|
|
};
|