jenkins-bot has submitted this change and it was merged.

Change subject: [BREAKING CHANGE] Make content isolation optional
......................................................................


[BREAKING CHANGE] Make content isolation optional

Changes:

WindowManager.js
* No longer require manager as an argument to window, and instead set the
  manager after the window is added and ready to be loaded
* Add isolate option, which configures managed windows to isolate their
  contents using iframes
* Added shouldIsolate method for checking isolate config option state
* Bonus: Add preparingTo(Open|Close) promise tracking to avoid the black
  hole that occurs while processing preparing promises
* Bonus: Made opening a window while another is preparing, opening or open
  return a rejected promise with an error instead of waiting for it to
  open, closing it, and then opening it again, which seemed evil
* Bonus: Made openWindow and closeWindow methods actually return promises

Window.js, Frame.js
* Merged OO.ui.Frame into Window.js
* Made isolation optional
* Remove border compensation from getContentHeight which was evil and broken

TextInputMenuWidget.js
* Updated use of widget.$.frame to widget.$.$frame to check if a widget is
  in a parent or child frame, and access the frame's iframe element

Window.less
* Restricted font and overlay styles for dialogs to isolated windows only

WindowManager.less
* Made frames always expand to fill their parent div, making them visually
  identical to their non-isolated counterparts

Window.less
* Merged in Frame.less styles

Element.js
* Switched from taking OO.ui.Frame to jQuery selections of iframe elements
  as an argument for referencing the frame jQuery is bound to

ProcessDialog.js, MessageDialog.js and Dialog.js
* Updated use of this.frame.* to this.* as per merging of OO.ui.Frame and
  OO.ui.Window

demos/dialog.js
* Add toggle for isolate mode

Change-Id: I88e5e7ac7f078ab52230f1d5918b90ffc2f893e8
---
M build/modules.json
M demos/dialogs.js
M jsduck.categories.json
M src/Dialog.js
M src/Element.js
D src/Frame.js
M src/Window.js
M src/WindowManager.js
M src/dialogs/MessageDialog.js
M src/dialogs/ProcessDialog.js
D src/styles/Frame.less
M src/styles/Window.less
M src/styles/WindowManager.less
D src/themes/apex/Frame.less
M src/themes/apex/Window.less
M src/widgets/TextInputMenuWidget.js
16 files changed, 452 insertions(+), 422 deletions(-)

Approvals:
  Catrope: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/build/modules.json b/build/modules.json
index 23c4660..fddc34a 100644
--- a/build/modules.json
+++ b/build/modules.json
@@ -6,7 +6,6 @@
 
                        "src/ActionSet.js",
                        "src/Element.js",
-                               "src/Frame.js",
                                "src/Layout.js",
                                "src/Widget.js",
                                "src/Window.js",
@@ -87,7 +86,6 @@
                ],
                "styles": [
                        "src/styles/core.less",
-                       "src/styles/Frame.less",
                        "src/styles/Toolbar.less",
                        "src/styles/ToolGroup.less",
                        "src/styles/Window.less",
@@ -138,7 +136,6 @@
        "oojs-ui-apex": {
                "styles": [
                        "src/themes/apex/Dialog.less",
-                       "src/themes/apex/Frame.less",
                        "src/themes/apex/Toolbar.less",
                        "src/themes/apex/ToolGroup.less",
                        "src/themes/apex/Window.less",
diff --git a/demos/dialogs.js b/demos/dialogs.js
index 6bb329b..f1a66a7 100644
--- a/demos/dialogs.js
+++ b/demos/dialogs.js
@@ -2,11 +2,14 @@
        var i, l, name, openButton, DialogClass, config,
                $demo = $( '.oo-ui-demo' ),
                fieldset = new OO.ui.FieldsetLayout( { label: 'Dialogs' } ),
+               isolateSwitch = new OO.ui.ToggleSwitchWidget(),
                windows = {},
-               windowManager = new OO.ui.WindowManager();
+               isolatedWindows = {},
+               windowManager = new OO.ui.WindowManager(),
+               isolatedWindowManager = new OO.ui.WindowManager( { isolate: 
true } );
 
-       function SimpleDialog( manager, config ) {
-               SimpleDialog.super.call( this, manager, config );
+       function SimpleDialog( config ) {
+               SimpleDialog.super.call( this, config );
        }
        OO.inheritClass( SimpleDialog, OO.ui.Dialog );
        SimpleDialog.static.title = 'Simple dialog';
@@ -15,7 +18,7 @@
                        dialog = this;
 
                SimpleDialog.super.prototype.initialize.apply( this, arguments 
);
-               this.content = new OO.ui.PanelLayout( { $: this.$, padded: true 
} );
+               this.content = new OO.ui.PanelLayout( { $: this.$, padded: 
true, expanded: false } );
                this.content.$element.append( '<p>Dialog content</p>' );
 
                closeButton = new OO.ui.ButtonWidget( {
@@ -29,9 +32,12 @@
                this.content.$element.append( closeButton.$element );
                this.$body.append( this.content.$element );
        };
+       SimpleDialog.prototype.getBodyHeight = function () {
+               return this.content.$element.outerHeight( true );
+       };
 
-       function ProcessDialog( manager, config ) {
-               ProcessDialog.super.call( this, manager, config );
+       function ProcessDialog( config ) {
+               ProcessDialog.super.call( this, config );
        }
        OO.inheritClass( ProcessDialog, OO.ui.ProcessDialog );
        ProcessDialog.static.title = 'Process dialog';
@@ -41,7 +47,7 @@
        ];
        ProcessDialog.prototype.initialize = function () {
                ProcessDialog.super.prototype.initialize.apply( this, arguments 
);
-               this.content = new OO.ui.PanelLayout( { $: this.$, padded: true 
} );
+               this.content = new OO.ui.PanelLayout( { $: this.$, padded: 
true, expanded: false } );
                this.content.$element.append( '<p>Dialog content</p>' );
                this.$body.append( this.content.$element );
        };
@@ -54,9 +60,12 @@
                }
                return ProcessDialog.super.prototype.getActionProcess.call( 
this, action );
        };
+       ProcessDialog.prototype.getBodyHeight = function () {
+               return this.content.$element.outerHeight( true );
+       };
 
-       function BrokenDialog( manager, config ) {
-               BrokenDialog.super.call( this, manager, config );
+       function BrokenDialog( config ) {
+               BrokenDialog.super.call( this, config );
                this.broken = false;
        }
        OO.inheritClass( BrokenDialog, OO.ui.ProcessDialog );
@@ -130,8 +139,8 @@
                        .setLabel( this.label );
        };
 
-       function BookletDialog( manager, config ) {
-               BookletDialog.super.call( this, manager, config );
+       function BookletDialog( config ) {
+               BookletDialog.super.call( this, config );
        }
        OO.inheritClass( BookletDialog, OO.ui.ProcessDialog );
        BookletDialog.static.title = 'Booklet dialog';
@@ -299,23 +308,35 @@
                        }
                }
        ];
+
+       function openDialog( name, data ) {
+               if ( isolateSwitch.getValue() ) {
+                       isolatedWindowManager.openWindow( name, data );
+               } else {
+                       windowManager.openWindow( name, data );
+               }
+       }
+
+       fieldset.addItems( [ new OO.ui.FieldLayout( isolateSwitch, { label: 
'Isolate dialogs', align: 'top' } ) ] );
        for ( i = 0, l = config.length; i < l; i++ ) {
                name = 'window_' + i;
                DialogClass = config[i].dialogClass || SimpleDialog;
-               windows[name] = new DialogClass( windowManager, 
config[i].config );
+               windows[name] = new DialogClass( config[i].config );
+               isolatedWindows[name] = new DialogClass( config[i].config );
                openButton = new OO.ui.ButtonWidget( {
                        framed: false,
                        icon: 'window',
                        label: config[i].name
                } );
                openButton.on(
-                       'click', OO.ui.bind( windowManager.openWindow, 
windowManager, name, config[i].data )
+                       'click', OO.ui.bind( openDialog, this, name, 
config[i].data )
                );
                fieldset.addItems( [ new OO.ui.FieldLayout( openButton, { 
align: 'inline' } ) ] );
        }
        windowManager.addWindows( windows );
+       isolatedWindowManager.addWindows( isolatedWindows );
 
        $demo.append( $( '<div class="oo-ui-demo-container"></div>' ).append(
-               fieldset.$element, windowManager.$element
+               fieldset.$element, windowManager.$element, 
isolatedWindowManager.$element
        ) );
 };
diff --git a/jsduck.categories.json b/jsduck.categories.json
index 92afb6c..7c19c1a 100644
--- a/jsduck.categories.json
+++ b/jsduck.categories.json
@@ -7,7 +7,6 @@
                                "classes": [
                                        "OO.ui",
                                        "OO.ui.Element",
-                                       "OO.ui.Frame",
                                        "OO.ui.Toolbar",
                                        "OO.ui.Window",
                                        "OO.ui.Dialog",
diff --git a/src/Dialog.js b/src/Dialog.js
index b507d41..66d7e48 100644
--- a/src/Dialog.js
+++ b/src/Dialog.js
@@ -27,9 +27,9 @@
  * @constructor
  * @param {Object} [config] Configuration options
  */
-OO.ui.Dialog = function OoUiDialog( manager, config ) {
+OO.ui.Dialog = function OoUiDialog( config ) {
        // Parent constructor
-       OO.ui.Dialog.super.call( this, manager, config );
+       OO.ui.Dialog.super.call( this, config );
 
        // Properties
        this.actions = new OO.ui.ActionSet();
@@ -102,7 +102,7 @@
  *
  * @param {jQuery.Event} e Key down event
  */
-OO.ui.Dialog.prototype.onFrameDocumentKeyDown = function ( e ) {
+OO.ui.Dialog.prototype.onDocumentKeyDown = function ( e ) {
        if ( e.which === OO.ui.Keys.ESCAPE ) {
                this.close();
                return false;
@@ -234,11 +234,11 @@
 
        // Events
        if ( this.constructor.static.escapable ) {
-               this.frame.$document.on( 'keydown', OO.ui.bind( 
this.onFrameDocumentKeyDown, this ) );
+               this.$document.on( 'keydown', OO.ui.bind( 
this.onDocumentKeyDown, this ) );
        }
 
        // Initialization
-       this.frame.$content.addClass( 'oo-ui-dialog-content' );
+       this.$content.addClass( 'oo-ui-dialog-content' );
 };
 
 /**
@@ -283,7 +283,7 @@
  */
 OO.ui.Dialog.prototype.pushPending = function () {
        if ( this.pending === 0 ) {
-               this.frame.$content.addClass( 
'oo-ui-actionDialog-content-pending' );
+               this.$content.addClass( 'oo-ui-actionDialog-content-pending' );
                this.$head.addClass( 'oo-ui-texture-pending' );
        }
        this.pending++;
@@ -300,7 +300,7 @@
  */
 OO.ui.Dialog.prototype.popPending = function () {
        if ( this.pending === 1 ) {
-               this.frame.$content.removeClass( 
'oo-ui-actionDialog-content-pending' );
+               this.$content.removeClass( 'oo-ui-actionDialog-content-pending' 
);
                this.$head.removeClass( 'oo-ui-texture-pending' );
        }
        this.pending = Math.max( 0, this.pending - 1 );
diff --git a/src/Element.js b/src/Element.js
index effc9d8..84bb2f9 100644
--- a/src/Element.js
+++ b/src/Element.js
@@ -56,18 +56,19 @@
  *
  * @static
  * @param {jQuery|HTMLElement|HTMLDocument|Window} context Context to bind the 
function to
- * @param {OO.ui.Frame} [frame] Frame of the document context
+ * @param {jQuery} [$iframe] HTML iframe element that contains the document, 
omit if document is
+ *   not in an iframe
  * @return {Function} Bound jQuery function
  */
-OO.ui.Element.getJQuery = function ( context, frame ) {
+OO.ui.Element.getJQuery = function ( context, $iframe ) {
        function wrapper( selector ) {
                return $( selector, wrapper.context );
        }
 
        wrapper.context = this.getDocument( context );
 
-       if ( frame ) {
-               wrapper.frame = frame;
+       if ( $iframe ) {
+               wrapper.$iframe = $iframe;
        }
 
        return wrapper;
diff --git a/src/Frame.js b/src/Frame.js
deleted file mode 100644
index 6611006..0000000
--- a/src/Frame.js
+++ /dev/null
@@ -1,264 +0,0 @@
-/**
- * Embedded iframe with the same styles as its parent.
- *
- * @class
- * @extends OO.ui.Element
- * @mixins OO.EventEmitter
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.Frame = function OoUiFrame( config ) {
-       // Parent constructor
-       OO.ui.Frame.super.call( this, config );
-
-       // Mixin constructors
-       OO.EventEmitter.call( this );
-
-       // Properties
-       this.loading = null;
-       this.config = config;
-       this.dir = null;
-
-       // Initialize
-       this.$element
-               .addClass( 'oo-ui-frame' )
-               .attr( { frameborder: 0, scrolling: 'no' } );
-
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.Frame, OO.ui.Element );
-OO.mixinClass( OO.ui.Frame, OO.EventEmitter );
-
-/* Static Properties */
-
-/**
- * @static
- * @inheritdoc
- */
-OO.ui.Frame.static.tagName = 'iframe';
-
-/* Events */
-
-/**
- * @event load
- */
-
-/* Static Methods */
-
-/**
- * Transplant the CSS styles from as parent document to a frame's document.
- *
- * This loops over the style sheets in the parent document, and copies their 
nodes to the
- * frame's document. It then polls the document to see when all styles have 
loaded, and once they
- * have, resolves the promise.
- *
- * If the styles still haven't loaded after a long time (5 seconds by 
default), we give up waiting
- * and resolve the promise anyway. This protects against cases like a display: 
none; iframe in
- * Firefox, where the styles won't load until the iframe becomes visible.
- *
- * For details of how we arrived at the strategy used in this function, see 
#load.
- *
- * @static
- * @inheritable
- * @param {HTMLDocument} parentDoc Document to transplant styles from
- * @param {HTMLDocument} frameDoc Document to transplant styles to
- * @param {number} [timeout=5000] How long to wait before giving up (in ms). 
If 0, never give up.
- * @return {jQuery.Promise} Promise resolved when styles have loaded
- */
-OO.ui.Frame.static.transplantStyles = function ( parentDoc, frameDoc, timeout 
) {
-       var i, numSheets, styleNode, styleText, newNode, timeoutID, pollNodeId, 
$pendingPollNodes,
-               $pollNodes = $( [] ),
-               // Fake font-family value
-               fontFamily = 'oo-ui-frame-transplantStyles-loaded',
-               nextIndex = parentDoc.oouiFrameTransplantStylesNextIndex || 0,
-               deferred = $.Deferred();
-
-       for ( i = 0, numSheets = parentDoc.styleSheets.length; i < numSheets; 
i++ ) {
-               styleNode = parentDoc.styleSheets[i].ownerNode;
-               if ( styleNode.disabled ) {
-                       continue;
-               }
-
-               if ( styleNode.nodeName.toLowerCase() === 'link' ) {
-                       // External stylesheet; use @import
-                       styleText = '@import url(' + styleNode.href + ');';
-               } else {
-                       // Internal stylesheet; just copy the text
-                       styleText = styleNode.textContent;
-               }
-
-               // Create a node with a unique ID that we're going to monitor 
to see when the CSS
-               // has loaded
-               if ( styleNode.oouiFrameTransplantStylesId ) {
-                       // If we're nesting transplantStyles operations and 
this node already has
-                       // a CSS rule to wait for loading, reuse it
-                       pollNodeId = styleNode.oouiFrameTransplantStylesId;
-               } else {
-                       // Otherwise, create a new ID
-                       pollNodeId = 'oo-ui-frame-transplantStyles-loaded-' + 
nextIndex;
-                       nextIndex++;
-
-                       // Add #pollNodeId { font-family: ... } to the end of 
the stylesheet / after the @import
-                       // The font-family rule will only take effect once the 
@import finishes
-                       styleText += '\n' + '#' + pollNodeId + ' { font-family: 
' + fontFamily + '; }';
-               }
-
-               // Create a node with id=pollNodeId
-               $pollNodes = $pollNodes.add( $( '<div>', frameDoc )
-                       .attr( 'id', pollNodeId )
-                       .appendTo( frameDoc.body )
-               );
-
-               // Add our modified CSS as a <style> tag
-               newNode = frameDoc.createElement( 'style' );
-               newNode.textContent = styleText;
-               newNode.oouiFrameTransplantStylesId = pollNodeId;
-               frameDoc.head.appendChild( newNode );
-       }
-       frameDoc.oouiFrameTransplantStylesNextIndex = nextIndex;
-
-       // Poll every 100ms until all external stylesheets have loaded
-       $pendingPollNodes = $pollNodes;
-       timeoutID = setTimeout( function pollExternalStylesheets() {
-               while (
-                       $pendingPollNodes.length > 0 &&
-                       $pendingPollNodes.eq( 0 ).css( 'font-family' ) === 
fontFamily
-               ) {
-                       $pendingPollNodes = $pendingPollNodes.slice( 1 );
-               }
-
-               if ( $pendingPollNodes.length === 0 ) {
-                       // We're done!
-                       if ( timeoutID !== null ) {
-                               timeoutID = null;
-                               $pollNodes.remove();
-                               deferred.resolve();
-                       }
-               } else {
-                       timeoutID = setTimeout( pollExternalStylesheets, 100 );
-               }
-       }, 100 );
-       // ...but give up after a while
-       if ( timeout !== 0 ) {
-               setTimeout( function () {
-                       if ( timeoutID ) {
-                               clearTimeout( timeoutID );
-                               timeoutID = null;
-                               $pollNodes.remove();
-                               deferred.reject();
-                       }
-               }, timeout || 5000 );
-       }
-
-       return deferred.promise();
-};
-
-/* Methods */
-
-/**
- * Load the frame contents.
- *
- * Once the iframe's stylesheets are loaded, the `load` event will be emitted 
and the returned
- * promise will be resolved. Calling while loading will return a promise but 
not trigger a new
- * loading cycle. Calling after loading is complete will return a promise 
that's already been
- * resolved.
- *
- * 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.
- *
- * In Chrome, stylesheets don't show up in document.styleSheets until they 
have loaded, so we could
- * just poll that array and wait for it to have the right length. However, in 
Firefox, stylesheets
- * are added to document.styleSheets immediately, and the only way you can 
determine whether they've
- * loaded is to attempt to access .cssRules and wait for that to stop throwing 
an exception. But
- * cross-domain stylesheets never allow .cssRules to be accessed even after 
they have loaded.
- *
- * The workaround is to change all `<link href="...">` tags to `<style>@import 
url(...)</style>` tags.
- * Because `@import` is blocking, Chrome won't add the stylesheet to 
document.styleSheets until
- * the `@import` has finished, and Firefox won't allow .cssRules to be 
accessed until the `@import`
- * has finished. And because the contents of the `<style>` tag are from the 
same origin, accessing
- * .cssRules is allowed.
- *
- * However, now that we control the styles we're injecting, we might as well 
do away with
- * browser-specific polling hacks like document.styleSheets and .cssRules, and 
instead inject
- * `<style>@import url(...); #foo { font-family: someValue; }</style>`, then 
create `<div id="foo">`
- * and wait for its font-family to change to someValue. Because `@import` is 
blocking, the font-family
- * rule is not applied until after the `@import` finishes.
- *
- * All this stylesheet injection and polling magic is in #transplantStyles.
- *
- * @return {jQuery.Promise} Promise resolved when loading is complete
- * @fires load
- */
-OO.ui.Frame.prototype.load = function () {
-       var win, doc,
-               frame = this;
-
-       // Return existing promise if already loading or loaded
-       if ( this.loading ) {
-               return this.loading.promise();
-       }
-
-       // Load the frame
-       this.loading = $.Deferred();
-
-       win = this.$element.prop( 'contentWindow' );
-       doc = win.document;
-
-       // Cache directionality
-       this.dir = OO.ui.Element.getDir( this.$element ) || 'ltr';
-
-       // Initialize contents
-       doc.open();
-       // The following classes can be used here:
-       // oo-ui-ltr
-       // oo-ui-rtl
-       doc.write(
-               '<!doctype html>' +
-               '<html>' +
-                       '<body class="oo-ui-frame-content oo-ui-' + 
this.getDir() + '" dir="' + this.getDir() + '">' +
-                       '</body>' +
-               '</html>'
-       );
-       doc.close();
-
-       // Properties
-       this.$ = OO.ui.Element.getJQuery( doc, this );
-       this.$content = this.$( '.oo-ui-frame-content' ).attr( 'tabIndex', 0 );
-       this.$document = this.$( doc );
-
-       // Initialization
-       this.constructor.static.transplantStyles( this.getElementDocument(), 
this.$document[0] )
-               .always( function () {
-                       frame.emit( 'load' );
-                       frame.loading.resolve();
-               } );
-
-       return this.loading.promise();
-};
-
-/**
- * Set the size of the frame.
- *
- * @param {number} width Frame width in pixels
- * @param {number} height Frame height in pixels
- * @chainable
- */
-OO.ui.Frame.prototype.setSize = function ( width, height ) {
-       this.$element.css( { width: width, height: height } );
-       return this;
-};
-
-/**
- * Get the directionality of the frame
- *
- * @return {string} Directionality, 'ltr' or 'rtl'
- */
-OO.ui.Frame.prototype.getDir = function () {
-       return this.dir;
-};
diff --git a/src/Window.js b/src/Window.js
index aa363a4..8a93fe5 100644
--- a/src/Window.js
+++ b/src/Window.js
@@ -34,15 +34,12 @@
  * If the requested size is not recognized, the window manager will choose a 
sensible fallback.
  *
  * @constructor
- * @param {OO.ui.WindowManager} manager Manager of window
  * @param {Object} [config] Configuration options
  * @cfg {string} [size] Symbolic name of dialog size, `small`, `medium`, 
`large` or `full`; omit to
  *   use #static-size
  * @fires initialize
  */
-OO.ui.Window = function OoUiWindow( manager, config ) {
-       var win = this;
-
+OO.ui.Window = function OoUiWindow( config ) {
        // Configuration initialization
        config = config || {};
 
@@ -52,46 +49,25 @@
        // Mixin constructors
        OO.EventEmitter.call( this );
 
-       if ( !( manager instanceof OO.ui.WindowManager ) ) {
-               throw new Error( 'Cannot construct window: window must have a 
manager' );
-       }
-
        // Properties
-       this.manager = manager;
+       this.manager = null;
        this.initialized = false;
        this.visible = false;
        this.opening = null;
        this.closing = null;
        this.opened = null;
        this.timing = null;
+       this.loading = null;
        this.size = config.size || this.constructor.static.size;
-       this.frame = new OO.ui.Frame( { $: this.$ } );
        this.$frame = this.$( '<div>' );
-       this.$ = function () {
-               throw new Error( 'this.$() cannot be used until the frame has 
been initialized.' );
-       };
 
        // Initialization
        this.$element
                .addClass( 'oo-ui-window' )
-               // Hide the window using visibility: hidden; while the iframe 
is still loading
-               // Can't use display: none; because that prevents the iframe 
from loading in Firefox
-               .css( 'visibility', 'hidden' )
                .append( this.$frame );
-       this.$frame
-               .addClass( 'oo-ui-window-frame' )
-               .append( this.frame.$element );
+       this.$frame.addClass( 'oo-ui-window-frame' );
 
-       // Events
-       this.frame.on( 'load', function () {
-               win.initialize();
-               win.initialized = true;
-               // Undo the visibility: hidden; hack and apply display: none;
-               // We can do this safely now that the iframe has initialized
-               // (don't do this from within #initialize because it has to 
happen
-               // after the all subclasses have been handled as well).
-               win.$element.hide().css( 'visibility', '' );
-       } );
+       // NOTE: Additional intitialization will occur when #setManager is 
called
 };
 
 /* Setup */
@@ -119,6 +95,116 @@
  */
 OO.ui.Window.static.size = 'medium';
 
+/* Static Methods */
+
+/**
+ * Transplant the CSS styles from as parent document to a frame's document.
+ *
+ * This loops over the style sheets in the parent document, and copies their 
nodes to the
+ * frame's document. It then polls the document to see when all styles have 
loaded, and once they
+ * have, resolves the promise.
+ *
+ * If the styles still haven't loaded after a long time (5 seconds by 
default), we give up waiting
+ * and resolve the promise anyway. This protects against cases like a display: 
none; iframe in
+ * Firefox, where the styles won't load until the iframe becomes visible.
+ *
+ * For details of how we arrived at the strategy used in this function, see 
#load.
+ *
+ * @static
+ * @inheritable
+ * @param {HTMLDocument} parentDoc Document to transplant styles from
+ * @param {HTMLDocument} frameDoc Document to transplant styles to
+ * @param {number} [timeout=5000] How long to wait before giving up (in ms). 
If 0, never give up.
+ * @return {jQuery.Promise} Promise resolved when styles have loaded
+ */
+OO.ui.Window.static.transplantStyles = function ( parentDoc, frameDoc, timeout 
) {
+       var i, numSheets, styleNode, styleText, newNode, timeoutID, pollNodeId, 
$pendingPollNodes,
+               $pollNodes = $( [] ),
+               // Fake font-family value
+               fontFamily = 'oo-ui-frame-transplantStyles-loaded',
+               nextIndex = parentDoc.oouiFrameTransplantStylesNextIndex || 0,
+               deferred = $.Deferred();
+
+       for ( i = 0, numSheets = parentDoc.styleSheets.length; i < numSheets; 
i++ ) {
+               styleNode = parentDoc.styleSheets[i].ownerNode;
+               if ( styleNode.disabled ) {
+                       continue;
+               }
+
+               if ( styleNode.nodeName.toLowerCase() === 'link' ) {
+                       // External stylesheet; use @import
+                       styleText = '@import url(' + styleNode.href + ');';
+               } else {
+                       // Internal stylesheet; just copy the text
+                       styleText = styleNode.textContent;
+               }
+
+               // Create a node with a unique ID that we're going to monitor 
to see when the CSS
+               // has loaded
+               if ( styleNode.oouiFrameTransplantStylesId ) {
+                       // If we're nesting transplantStyles operations and 
this node already has
+                       // a CSS rule to wait for loading, reuse it
+                       pollNodeId = styleNode.oouiFrameTransplantStylesId;
+               } else {
+                       // Otherwise, create a new ID
+                       pollNodeId = 'oo-ui-frame-transplantStyles-loaded-' + 
nextIndex;
+                       nextIndex++;
+
+                       // Add #pollNodeId { font-family: ... } to the end of 
the stylesheet / after the @import
+                       // The font-family rule will only take effect once the 
@import finishes
+                       styleText += '\n' + '#' + pollNodeId + ' { font-family: 
' + fontFamily + '; }';
+               }
+
+               // Create a node with id=pollNodeId
+               $pollNodes = $pollNodes.add( $( '<div>', frameDoc )
+                       .attr( 'id', pollNodeId )
+                       .appendTo( frameDoc.body )
+               );
+
+               // Add our modified CSS as a <style> tag
+               newNode = frameDoc.createElement( 'style' );
+               newNode.textContent = styleText;
+               newNode.oouiFrameTransplantStylesId = pollNodeId;
+               frameDoc.head.appendChild( newNode );
+       }
+       frameDoc.oouiFrameTransplantStylesNextIndex = nextIndex;
+
+       // Poll every 100ms until all external stylesheets have loaded
+       $pendingPollNodes = $pollNodes;
+       timeoutID = setTimeout( function pollExternalStylesheets() {
+               while (
+                       $pendingPollNodes.length > 0 &&
+                       $pendingPollNodes.eq( 0 ).css( 'font-family' ) === 
fontFamily
+               ) {
+                       $pendingPollNodes = $pendingPollNodes.slice( 1 );
+               }
+
+               if ( $pendingPollNodes.length === 0 ) {
+                       // We're done!
+                       if ( timeoutID !== null ) {
+                               timeoutID = null;
+                               $pollNodes.remove();
+                               deferred.resolve();
+                       }
+               } else {
+                       timeoutID = setTimeout( pollExternalStylesheets, 100 );
+               }
+       }, 100 );
+       // ...but give up after a while
+       if ( timeout !== 0 ) {
+               setTimeout( function () {
+                       if ( timeoutID ) {
+                               clearTimeout( timeoutID );
+                               timeoutID = null;
+                               $pollNodes.remove();
+                               deferred.reject();
+                       }
+               }, timeout || 5000 );
+       }
+
+       return deferred.promise();
+};
+
 /* Methods */
 
 /**
@@ -137,6 +223,24 @@
  */
 OO.ui.Window.prototype.isVisible = function () {
        return this.visible;
+};
+
+/**
+ * Check if window is loading.
+ *
+ * @return {boolean} Window is loading
+ */
+OO.ui.Window.prototype.isLoading = function () {
+       return this.loading && this.loading.state() === 'pending';
+};
+
+/**
+ * Check if window is loaded.
+ *
+ * @return {boolean} Window is loaded
+ */
+OO.ui.Window.prototype.isLoaded = function () {
+       return this.loading && this.loading.state() === 'resolved';
 };
 
 /**
@@ -182,15 +286,6 @@
 };
 
 /**
- * Get the window frame.
- *
- * @return {OO.ui.Frame} Frame of window
- */
-OO.ui.Window.prototype.getFrame = function () {
-       return this.frame;
-};
-
-/**
  * Get the window size.
  *
  * @return {string} Symbolic size name, e.g. 'small', 'medium', 'large', 'full'
@@ -207,8 +302,8 @@
 OO.ui.Window.prototype.getContentHeight = function () {
        return Math.round(
                // Add buffer for border
-               ( ( this.$frame.outerHeight() - this.$frame.innerHeight() ) * 2 
) +
-               // Height of contents
+               ( this.$frame.outerHeight() - this.$frame.innerHeight() ) +
+               // Use combined heights of children
                ( this.$head.outerHeight( true ) + this.getBodyHeight() + 
this.$foot.outerHeight( true ) )
        );
 };
@@ -220,6 +315,15 @@
  */
 OO.ui.Window.prototype.getBodyHeight = function () {
        return this.$body[0].scrollHeight;
+};
+
+/**
+ * Get the directionality of the frame
+ *
+ * @return {string} Directionality, 'ltr' or 'rtl'
+ */
+OO.ui.Window.prototype.getDir = function () {
+       return this.dir;
 };
 
 /**
@@ -291,6 +395,76 @@
 };
 
 /**
+ * Toggle visibility of window.
+ *
+ * If the window is isolated and hasn't fully loaded yet, the visiblity 
property will be used
+ * instead of display.
+ *
+ * @param {boolean} [show] Make window visible, omit to toggle visibility
+ * @fires visible
+ * @chainable
+ */
+OO.ui.Window.prototype.toggle = function ( show ) {
+       show = show === undefined ? !this.visible : !!show;
+
+       if ( show !== this.isVisible() ) {
+               this.visible = show;
+
+               if ( this.isolated && !this.isLoaded() ) {
+                       // Hide the window using visibility instead of display 
until loading is complete
+                       // Can't use display: none; because that prevents the 
iframe from loading in Firefox
+                       this.$element.css( 'visibility', show ? 'visible' : 
'hidden' );
+               } else {
+                       this.$element.toggle( show ).css( 'visibility', '' );
+               }
+               this.emit( 'toggle', show );
+       }
+
+       return this;
+};
+
+/**
+ * Set the window manager.
+ *
+ * This must be called before initialize. Calling it more than once will cause 
an error.
+ *
+ * @param {OO.ui.WindowManager} manager Manager for this window
+ * @throws {Error} If called more than once
+ * @chainable
+ */
+OO.ui.Window.prototype.setManager = function ( manager ) {
+       if ( this.manager ) {
+               throw new Error( 'Cannot set window manager, window already has 
a manager' );
+       }
+
+       // Properties
+       this.manager = manager;
+       this.isolated = manager.shouldIsolate();
+
+       // Initialization
+       if ( this.isolated ) {
+               this.$iframe = this.$( '<iframe>' );
+               this.$iframe.attr( { frameborder: 0, scrolling: 'no' } );
+               this.$frame.append( this.$iframe );
+               this.$ = function () {
+                       throw new Error( 'this.$() cannot be used until the 
frame has been initialized.' );
+               };
+               // WARNING: Do not use this.$ again until #initialize is called
+       } else {
+               this.$content = this.$( '<div>' );
+               this.$document = $( this.getElementDocument() );
+               this.$content.addClass( 'oo-ui-window-content' );
+               this.$frame.append( this.$content );
+       }
+       this.toggle( false );
+
+       // Figure out directionality:
+       this.dir = OO.ui.Element.getDir( this.$iframe || this.$content ) || 
'ltr';
+
+       return this;
+};
+
+/**
  * Set the window size.
  *
  * @param {string} size Symbolic size name, e.g. 'small', 'medium', 'large', 
'full'
@@ -339,11 +513,15 @@
  *
  * Once this method is called, this.$ can be used to create elements within 
the frame.
  *
+ * @throws {Error} If not attached to a manager
  * @chainable
  */
 OO.ui.Window.prototype.initialize = function () {
+       if ( !this.manager ) {
+               throw new Error( 'Cannot initialize window, must be attached to 
a manager' );
+       }
+
        // Properties
-       this.$ = this.frame.$;
        this.$head = this.$( '<div>' );
        this.$body = this.$( '<div>' );
        this.$foot = this.$( '<div>' );
@@ -354,9 +532,7 @@
        this.$body.addClass( 'oo-ui-window-body' );
        this.$foot.addClass( 'oo-ui-window-foot' );
        this.$overlay.addClass( 'oo-ui-window-overlay' );
-       this.frame.$content
-               .addClass( 'oo-ui-window-content' )
-               .append( this.$head, this.$body, this.$foot, this.$overlay );
+       this.$content.append( this.$head, this.$body, this.$foot, this.$overlay 
);
 
        return this;
 };
@@ -389,18 +565,6 @@
 };
 
 /**
- * Load window.
- *
- * This is called by OO.ui.WindowManager durring window adding, and should not 
be called directly
- * by other systems.
- *
- * @return {jQuery.Promise} Promise resolved when window is loaded
- */
-OO.ui.Window.prototype.load = function () {
-       return this.frame.load();
-};
-
-/**
  * Setup window.
  *
  * This is called by OO.ui.WindowManager durring window opening, and should 
not be called directly
@@ -416,10 +580,9 @@
        this.$element.show();
        this.visible = true;
        this.getSetupProcess( data ).execute().done( function () {
-               win.manager.updateWindowSize( win );
                // Force redraw by asking the browser to measure the elements' 
widths
                win.$element.addClass( 'oo-ui-window-setup' ).width();
-               win.frame.$content.addClass( 'oo-ui-window-content-setup' 
).width();
+               win.$content.addClass( 'oo-ui-window-content-setup' ).width();
                deferred.resolve();
        } );
 
@@ -439,11 +602,11 @@
        var win = this,
                deferred = $.Deferred();
 
-       this.frame.$content[0].focus();
+       this.$content.focus();
        this.getReadyProcess( data ).execute().done( function () {
                // Force redraw by asking the browser to measure the elements' 
widths
                win.$element.addClass( 'oo-ui-window-ready' ).width();
-               win.frame.$content.addClass( 'oo-ui-window-content-ready' 
).width();
+               win.$content.addClass( 'oo-ui-window-content-ready' ).width();
                deferred.resolve();
        } );
 
@@ -464,13 +627,17 @@
                deferred = $.Deferred();
 
        this.getHoldProcess( data ).execute().done( function () {
-               var $focused = win.frame.$content.find( ':focus' );
-               if ( $focused.length ) {
-                       $focused[0].blur();
+               // Get the focused element within the window's content
+               var $focus = win.$content.find( OO.ui.Element.getDocument( 
win.$content ).activeElement );
+
+               // Blur the focused element
+               if ( $focus.length ) {
+                       $focus[0].blur();
                }
+
                // Force redraw by asking the browser to measure the elements' 
widths
                win.$element.removeClass( 'oo-ui-window-ready' ).width();
-               win.frame.$content.removeClass( 'oo-ui-window-content-ready' 
).width();
+               win.$content.removeClass( 'oo-ui-window-content-ready' 
).width();
                deferred.resolve();
        } );
 
@@ -493,7 +660,7 @@
        this.getTeardownProcess( data ).execute().done( function () {
                // Force redraw by asking the browser to measure the elements' 
widths
                win.$element.removeClass( 'oo-ui-window-setup' ).width();
-               win.frame.$content.removeClass( 'oo-ui-window-content-setup' 
).width();
+               win.$content.removeClass( 'oo-ui-window-content-setup' 
).width();
                win.$element.hide();
                win.visible = false;
                deferred.resolve();
@@ -501,3 +668,100 @@
 
        return deferred.promise();
 };
+
+/**
+ * Load the frame contents.
+ *
+ * Once the iframe's stylesheets are loaded, the `load` event will be emitted 
and the returned
+ * promise will be resolved. Calling while loading will return a promise but 
not trigger a new
+ * loading cycle. Calling after loading is complete will return a promise 
that's already been
+ * resolved.
+ *
+ * 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.
+ *
+ * In Chrome, stylesheets don't show up in document.styleSheets until they 
have loaded, so we could
+ * just poll that array and wait for it to have the right length. However, in 
Firefox, stylesheets
+ * are added to document.styleSheets immediately, and the only way you can 
determine whether they've
+ * loaded is to attempt to access .cssRules and wait for that to stop throwing 
an exception. But
+ * cross-domain stylesheets never allow .cssRules to be accessed even after 
they have loaded.
+ *
+ * The workaround is to change all `<link href="...">` tags to `<style>@import 
url(...)</style>`
+ * tags. Because `@import` is blocking, Chrome won't add the stylesheet to 
document.styleSheets
+ * until the `@import` has finished, and Firefox won't allow .cssRules to be 
accessed until the
+ * `@import` has finished. And because the contents of the `<style>` tag are 
from the same origin,
+ * accessing .cssRules is allowed.
+ *
+ * However, now that we control the styles we're injecting, we might as well 
do away with
+ * browser-specific polling hacks like document.styleSheets and .cssRules, and 
instead inject
+ * `<style>@import url(...); #foo { font-family: someValue; }</style>`, then 
create `<div id="foo">`
+ * and wait for its font-family to change to someValue. Because `@import` is 
blocking, the
+ * font-family rule is not applied until after the `@import` finishes.
+ *
+ * All this stylesheet injection and polling magic is in #transplantStyles.
+ *
+ * @return {jQuery.Promise} Promise resolved when loading is complete
+ * @fires load
+ */
+OO.ui.Window.prototype.load = function () {
+       var sub, doc, loading,
+               win = this;
+
+       // Non-isolated windows are already "loaded"
+       if ( !this.loading && !this.isolated ) {
+               this.loading = $.Deferred().resolve();
+               this.initialize();
+               // Set initialized state after so sub-classes aren't confused 
by it being set by calling
+               // their parent initialize method
+               this.initialized = true;
+       }
+
+       // Return existing promise if already loading or loaded
+       if ( this.loading ) {
+               return this.loading.promise();
+       }
+
+       // Load the frame
+       loading = this.loading = $.Deferred();
+       sub = this.$iframe.prop( 'contentWindow' );
+       doc = sub.document;
+
+       // Initialize contents
+       doc.open();
+       doc.write(
+               '<!doctype html>' +
+               '<html>' +
+                       '<body class="oo-ui-window-isolated 
oo-ui-window-content oo-ui-' + this.dir + '"' +
+                               ' style="direction:' + this.dir + ';" dir="' + 
this.dir + '">' +
+                       '</body>' +
+               '</html>'
+       );
+       doc.close();
+
+       // Properties
+       this.$ = OO.ui.Element.getJQuery( doc, this.$element );
+       this.$content = this.$( '.oo-ui-window-content' ).attr( 'tabIndex', 0 );
+       this.$document = this.$( doc );
+
+       // Initialization
+       this.constructor.static.transplantStyles( this.getElementDocument(), 
this.$document[0] )
+               .always( function () {
+                       // Initialize isolated windows
+                       win.initialize();
+                       // Set initialized state after so sub-classes aren't 
confused by it being set by calling
+                       // their parent initialize method
+                       win.initialized = true;
+                       // Undo the visibility: hidden; hack and apply display: 
none;
+                       // We can do this safely now that the iframe has 
initialized
+                       // (don't do this from within #initialize because it 
has to happen
+                       // after the all subclasses have been handled as well).
+                       win.toggle( win.isVisible() );
+
+                       loading.resolve();
+               } );
+
+       return loading.promise();
+};
diff --git a/src/WindowManager.js b/src/WindowManager.js
index 43516e9..37dbb25 100644
--- a/src/WindowManager.js
+++ b/src/WindowManager.js
@@ -38,6 +38,7 @@
  *
  * @constructor
  * @param {Object} [config] Configuration options
+ * @cfg {boolean} [isolate] Configure managed windows to isolate their content 
using inline frames
  * @cfg {OO.Factory} [factory] Window factory to use for automatic 
instantiation
  * @cfg {boolean} [modal=true] Prevent interaction outside the dialog
  */
@@ -54,10 +55,13 @@
        // Properties
        this.factory = config.factory;
        this.modal = config.modal === undefined || !!config.modal;
+       this.isolate = !!config.isolate;
        this.windows = {};
        this.opening = null;
        this.opened = null;
        this.closing = null;
+       this.preparingToOpen = null;
+       this.preparingToClose = null;
        this.size = null;
        this.currentWindow = null;
        this.$ariaHidden = null;
@@ -228,6 +232,17 @@
 };
 
 /**
+ * Check if window contents should be isolated.
+ *
+ * Window content isolation is done using inline frames.
+ *
+ * @return {boolean} Window contents should be isolated
+ */
+OO.ui.WindowManager.prototype.shouldIsolate = function () {
+       return this.isolate;
+};
+
+/**
  * Check if a window is being managed.
  *
  * @param {OO.ui.Window} win Window to check
@@ -295,8 +310,7 @@
  * If window is not yet instantiated, it will be instantiated and added 
automatically.
  *
  * @param {string} name Symbolic window name
- * @return {jQuery.Promise} Promise resolved when window is ready to be 
accessed; when resolved the
- *   first argument is an OO.ui.Window; when rejected the first argument is an 
OO.ui.Error
+ * @return {jQuery.Promise} Promise resolved with matching window, or rejected 
with an OO.ui.Error
  * @throws {Error} If the symbolic name is unrecognized by the factory
  * @throws {Error} If the symbolic name unrecognized as a managed window
  */
@@ -312,10 +326,8 @@
                                ) );
                        } else {
                                win = this.factory.create( name, this, { $: 
this.$ } );
-                               this.addWindows( [ win ] ).then(
-                                       OO.ui.bind( deferred.resolve, deferred, 
win ),
-                                       deferred.reject
-                               );
+                               this.addWindows( [ win ] );
+                               deferred.resolve( win );
                        }
                } else {
                        deferred.reject( new OO.ui.Error(
@@ -364,23 +376,30 @@
                opening.reject( new OO.ui.Error(
                        'Cannot open window: window is not attached to manager'
                ) );
+       } else if ( this.preparingToOpen || this.opening || this.opened ) {
+               opening.reject( new OO.ui.Error(
+                       'Cannot open window: another window is opening or open'
+               ) );
        }
 
        // Window opening
        if ( opening.state() !== 'rejected' ) {
-               // Begin loading the window if it's not loaded already - may 
take noticable time and we want
-               // too do this in paralell with any preparatory actions
-               preparing.push( win.load() );
+               // Begin loading the window if it's not loading or loaded 
already - may take noticable time
+               // and we want to do this in paralell with any other 
preparatory actions
+               if ( !win.isLoading() && !win.isLoaded() ) {
+                       // Finish initializing the window (must be done after 
manager is attached to DOM)
+                       win.setManager( this );
+                       preparing.push( win.load() );
+               }
 
-               if ( this.opening || this.opened ) {
-                       // If a window is currently opening or opened, close it 
first
-                       preparing.push( this.closeWindow( this.currentWindow ) 
);
-               } else if ( this.closing ) {
+               if ( this.closing ) {
                        // If a window is currently closing, wait for it to 
complete
                        preparing.push( this.closing );
                }
 
-               $.when.apply( $, preparing ).done( function () {
+               this.preparingToOpen = $.when.apply( $, preparing );
+               // Ensure handlers get called after preparingToOpen is set
+               this.preparingToOpen.done( function () {
                        if ( manager.modal ) {
                                manager.$( manager.getElementDocument() ).on( {
                                        // Prevent scrolling by keys in 
top-level window
@@ -400,13 +419,15 @@
                        }
                        manager.currentWindow = win;
                        manager.opening = opening;
+                       manager.preparingToOpen = null;
                        manager.emit( 'opening', win, opening, data );
-                       manager.updateWindowSize( win );
                        setTimeout( function () {
                                win.setup( data ).then( function () {
+                                       manager.updateWindowSize( win );
                                        manager.opening.notify( { state: 
'setup' } );
                                        setTimeout( function () {
                                                win.ready( data ).then( 
function () {
+                                                       
manager.updateWindowSize( win );
                                                        manager.opening.notify( 
{ state: 'ready' } );
                                                        manager.opening = null;
                                                        manager.opened = 
$.Deferred();
@@ -418,7 +439,7 @@
                } );
        }
 
-       return opening;
+       return opening.promise();
 };
 
 /**
@@ -453,7 +474,7 @@
                closing.reject( new OO.ui.Error(
                        'Cannot close window: window already closed with 
different data'
                ) );
-       } else if ( this.closing ) {
+       } else if ( this.preparingToClose || this.closing ) {
                closing.reject( new OO.ui.Error(
                        'Cannot close window: window already closing with 
different data'
                ) );
@@ -466,9 +487,11 @@
                        preparing.push( this.opening );
                }
 
-               // Close the window
-               $.when.apply( $, preparing ).done( function () {
+               this.preparingToClose = $.when.apply( $, preparing );
+               // Ensure handlers get called after preparingToClose is set
+               this.preparingToClose.done( function () {
                        manager.closing = closing;
+                       manager.preparingToClose = null;
                        manager.emit( 'closing', win, closing, data );
                        manager.opened = null;
                        opened.resolve( closing.promise(), data );
@@ -505,23 +528,18 @@
                } );
        }
 
-       return closing;
+       return closing.promise();
 };
 
 /**
  * Add windows.
  *
- * If the window manager is attached to the DOM then windows will be 
automatically loaded as they
- * are added.
- *
  * @param {Object.<string,OO.ui.Window>|OO.ui.Window[]} windows Windows to add
- * @return {jQuery.Promise} Promise resolved when all windows are added
  * @throws {Error} If one of the windows being added without an explicit 
symbolic name does not have
  *   a statically configured symbolic name
  */
 OO.ui.WindowManager.prototype.addWindows = function ( windows ) {
-       var i, len, win, name, list,
-               promises = [];
+       var i, len, win, name, list;
 
        if ( $.isArray( windows ) ) {
                // Convert to map of windows by looking up symbolic names from 
static configuration
@@ -542,13 +560,7 @@
                win = list[name];
                this.windows[name] = win;
                this.$element.append( win.$element );
-
-               if ( this.isElementAttached() ) {
-                       promises.push( win.load() );
-               }
        }
-
-       return $.when.apply( $, promises );
 };
 
 /**
diff --git a/src/dialogs/MessageDialog.js b/src/dialogs/MessageDialog.js
index 964732c..1a1814c 100644
--- a/src/dialogs/MessageDialog.js
+++ b/src/dialogs/MessageDialog.js
@@ -11,9 +11,9 @@
  * @constructor
  * @param {Object} [config] Configuration options
  */
-OO.ui.MessageDialog = function OoUiMessageDialog( manager, config ) {
+OO.ui.MessageDialog = function OoUiMessageDialog( config ) {
        // Parent constructor
-       OO.ui.MessageDialog.super.call( this, manager, config );
+       OO.ui.MessageDialog.super.call( this, config );
 
        // Properties
        this.verticalActionLayout = null;
@@ -159,7 +159,7 @@
 
        // Initialization
        this.title.$element.addClass( 'oo-ui-messageDialog-title' );
-       this.frame.$content.addClass( 'oo-ui-messageDialog-content' );
+       this.$content.addClass( 'oo-ui-messageDialog-content' );
        this.container.$element.append( this.text.$element );
        this.text.$element.append( this.title.$element, this.message.$element );
        this.$body.append( this.container.$element );
diff --git a/src/dialogs/ProcessDialog.js b/src/dialogs/ProcessDialog.js
index 2e0dd27..18a1ee2 100644
--- a/src/dialogs/ProcessDialog.js
+++ b/src/dialogs/ProcessDialog.js
@@ -24,9 +24,9 @@
  * @constructor
  * @param {Object} [config] Configuration options
  */
-OO.ui.ProcessDialog = function OoUiProcessDialog( manager, config ) {
+OO.ui.ProcessDialog = function OoUiProcessDialog( config ) {
        // Parent constructor
-       OO.ui.ProcessDialog.super.call( this, manager, config );
+       OO.ui.ProcessDialog.super.call( this, config );
 
        // Initialization
        this.$element.addClass( 'oo-ui-processDialog' );
@@ -109,7 +109,7 @@
        this.$errors
                .addClass( 'oo-ui-processDialog-errors' )
                .append( this.$errorsTitle, this.dismissButton.$element, 
this.retryButton.$element );
-       this.frame.$content
+       this.$content
                .addClass( 'oo-ui-processDialog-content' )
                .append( this.$errors );
        this.$navigation
diff --git a/src/styles/Frame.less b/src/styles/Frame.less
deleted file mode 100644
index dc0525c..0000000
--- a/src/styles/Frame.less
+++ /dev/null
@@ -1,14 +0,0 @@
-.oo-ui-frame {
-       margin: 0;
-       padding: 0;
-
-       &-content {
-               margin: 0;
-               padding: 0;
-       }
-
-       /* Content div takes focus when opened, so hide outline  */
-       &-content:focus {
-               outline: none;
-       }
-}
diff --git a/src/styles/Window.less b/src/styles/Window.less
index ca384f8..3eaee52 100644
--- a/src/styles/Window.less
+++ b/src/styles/Window.less
@@ -3,13 +3,20 @@
 .oo-ui-window {
        line-height: 1em;
 
-       & > &-frame {
+       &-frame {
                .oo-ui-box-sizing(border-box);
 
-                > .oo-ui-frame {
+                > iframe {
                        width: 100%;
                        height: 100%;
+                       margin: 0;
+                       padding: 0;
                }
+       }
+
+       /* Content div takes focus when opened, so hide outline */
+       &-content:focus {
+               outline: none;
        }
 
        &-head,
@@ -17,6 +24,12 @@
                .oo-ui-unselectable();
        }
 
+       &-body {
+               margin: 0;
+               padding: 0;
+               background: none;
+       }
+
        &-overlay {
                position: absolute;
                top: 0;
diff --git a/src/styles/WindowManager.less b/src/styles/WindowManager.less
index 1232a63..dc625d3 100644
--- a/src/styles/WindowManager.less
+++ b/src/styles/WindowManager.less
@@ -22,6 +22,11 @@
                                overflow: hidden;
                                max-width: 100%;
                                max-height: 100%;
+
+                               > iframe {
+                                       width: 100%;
+                                       height: 100%;
+                               }
                        }
                }
        }
diff --git a/src/themes/apex/Frame.less b/src/themes/apex/Frame.less
deleted file mode 100644
index fb19a77..0000000
--- a/src/themes/apex/Frame.less
+++ /dev/null
@@ -1,6 +0,0 @@
-.oo-ui-frame {
-       &-content {
-               font-family: sans-serif;
-               font-size: 0.8em;
-       }
-}
diff --git a/src/themes/apex/Window.less b/src/themes/apex/Window.less
index cc6866d..21544f9 100644
--- a/src/themes/apex/Window.less
+++ b/src/themes/apex/Window.less
@@ -1,11 +1,13 @@
 @import '../../styles/mixins';
 
 .oo-ui-window {
-       &-content {
+       &-isolated&-content {
+               font-family: sans-serif;
+               font-size: 0.8em;
                background: transparent;
        }
 
-       &-overlay {
+       &-isolated&-overlay {
                font-family: sans-serif;
                line-height: 1.5em;
                font-size: 1em;
diff --git a/src/widgets/TextInputMenuWidget.js 
b/src/widgets/TextInputMenuWidget.js
index 42e1ba4..d8e2299 100644
--- a/src/widgets/TextInputMenuWidget.js
+++ b/src/widgets/TextInputMenuWidget.js
@@ -77,9 +77,9 @@
        dimensions.top += $container.height();
 
        // Compensate for frame position if in a differnt frame
-       if ( this.input.$.frame && this.input.$.context !== 
this.$element[0].ownerDocument ) {
+       if ( this.input.$.$iframe && this.input.$.context !== 
this.$element[0].ownerDocument ) {
                frameOffset = OO.ui.Element.getRelativePosition(
-                       this.input.$.frame.$element, 
this.$element.offsetParent()
+                       this.input.$.$iframe, this.$element.offsetParent()
                );
                dimensions.left += frameOffset.left;
                dimensions.top += frameOffset.top;

-- 
To view, visit https://gerrit.wikimedia.org/r/148761
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I88e5e7ac7f078ab52230f1d5918b90ffc2f893e8
Gerrit-PatchSet: 21
Gerrit-Project: oojs/ui
Gerrit-Branch: master
Gerrit-Owner: Trevor Parscal <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Esanders <[email protected]>
Gerrit-Reviewer: Jforrester <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to