Files
palemoon27/dom/base/nsFrameLoader.cpp
T
roytam1 f18df9acf8 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1239496 - Load the UA sheet lazily so we can invalidate it for pref changes. Make it depend on the "layout.css.grid.enabled" pref. r=heycam (e4a1ac18b4)
- Bug 1225192 - fix eslint complaints about css-logic.js; r=pbrosset (ef19819c21)
- Bug 1225192 - remove leading whitespace before prettifying css; r=pbrosset (6b39d08907)
- Bug 1230491 - rewrite CssLogic.isContentStylesheet; r=bgrins (14c86cee67)
- Bug 1230491 - add CSSStyleSheet::parsingMode; r=heycam,bz (9ecabe232e)
- Bug 1239336 - set parsing mode on about:PreferenceStyleSheet; r=heycam,bgrins (cac07e099f)
- Bug 1224433 - Part 1: Add reftest. r=roc (28c8a47812)
- Bug 1195491 - use nsCOMPtr in RDFContentSinkImpl; r=bsmedberg (9804abe22e)
- Bug 1205713 - Merge ImageHostOverlay/ImageClientOverlay to ImageHost/ImageClient r=nical (2871e70466)
- Bug 1234472 - Add gonk sideband stream handling to gfx ipc r=nical (559f788a10)
- Bug 1049296 - Handle Sideband stream compositing in HwcComposer2D r=mwu,nical (b6dbac1a1b)
- Bug 1237508 - Odin: move GC allocation up to fix spurious hazard warning (there was an AutoKeepAtoms already) (r=hazard-red) (b5d0e54993)
- Bug 1229399: Group all IR's enums into a single one; r=luke (a7cfedc16a)
- Bug 1229399: Make {Get,Set}{Loc,Glo} opcodes type-independent; r=luke (0ed8e44098)
- Bug 1239211 - Odin: tweak wasm::Decoder (r=bbouvier) (980e08dc1e)
- Bug 1229399: Implement write/readVarU32 and use it for locals/globals; r=luke (5a6f654a14)
- Bug 1229399: Unite all the Emit* functions in WasmIonCompile; r=luke (64e517d18f)
- Bug 1240524: Fix AsmJS checkedValueType of Bool32x4; r=luke (d98ee763c7)
- Bug 1229399: Add list of wasm opcodes described in v8 design document; r=luke (1e05832624)
- Bug 1241339 - ObserverService may keep Console object alive until shutdown, r=baku (330d9e7bc0)
- Bug 1239177 - Odin: make calls more like wasm (r=bbouvier) (4bab27c7bc)
- Bug 1234985 - Odin: hoist read/writeOp into wasm::Decoder/Encoder (r=bbouvier) (de3ba3c3f9)
- Bug 1229399: Add test file forgotten in previous rebase/checkin; r=bustage (a2bdda0e86)
- Bug 1234985 - Odin: tweak ModuleGenerator interface for Baldr use (r=bbouvier) (5f52fd6777)
- Bug 1234985 - BaldrMonkey: testing-only (not content visible) wasm hello world (r=bbouvier) (e49e7f4db2)
- Bug 1234985 - Fix js/src/asmjs build errors with unified builds disabled. r=luke (acb7954471)
- Bug 1231335 - part 1, runtime switch for SAB+Atomics. r=waldo (973eb7edc1)
- Bug 1231335 - part 2, asm.js runtime SAB+Atomics gating. r=luke (b40a8d0b3f)
- Bug 1225040 - generalize jsapi-tests for SAB. r=arai (29240f91e1)
- Bug 1232211 - Don't perform various SharedArrayBuffer-centric typed array tests in builds with SharedArrayBuffer disabled (i.e. Aurora at next uplift). rs=lth over IRC, responding to cassandra!philor's concerns (f3df4b0c17)
- Bug 1231335 - part 3, testing functions and test cases. r=waldo (b91910ea28)
- Bug 1231335 - part 4, js shell. r=waldo (4a96addbde)
- Bug 1234985 - Odin: move mutedError and displayURL into AsmJSModule (r=bbouvier) (6be0172a15)
- Bug 1229399: Remove expression statements opcodes in wasm; r=luke (a046188e98)
- Bug 1229399: Allow to peek into the internal wasm IR; r=luke (f4b0497d89)
- Bug 1234985 - Odin: make names optional and supplied at the end (r=b ouvier) (ee7864fe7e)
- Bug 1238679 - Rename s/SIMD/Simd/ in type and function names. r=bbouvier (1d6c277644)
- Bug 1238679 - Add enum class SIMDOperation. r=bbouvier (eb7ce840da)
- Bug 1187232 - Access TypedArray's [[ArrayLength]] instead of performing property access; r=Waldo (a66850e72f)
- Bug 1239068 - Inline "PossiblyTypedArrayLength" intrinsic; r=Waldo (1a39045055)
- Bug 1238679 - Put JSJitInfo::depth in anonymous union. r=nbp (64d6859ee2)
- Bug 1238679 - Provide per-operation JSJitInfo for the SIMD functions. r=bbouvier (cd242c1f5d)
- Backed out changeset 7bda46f81215 (bug 1225031) for spidermonkey test failures (7c7de9af43)
- Bug 1225031 - get rid of the AnyTypedArray abstraction (updated). r=waldo (a8e3593280)
- Bug 1237284: Make inlineSimd* functions take MIRType to avoid an indirection; r=jolesen (c046f5ac06)
- Bug 1238679 - Make inlineSimdLoad/Store take a MIRType argument. r=bbouvier (7aceb59fc1)
- Bug 1238679 - Implement main SIMD inlining dispatch. r=bbouvier (3b19775b14)
- Bug 1237445 - Use GCHashMap for VMWrapperMap, r=terrence (4a5b3a1575)
- Bug 1241454: Hoist SimdTypeDescr::Type into SimdType, unify AsmJSSimdType and SimdType; r=jolesen (6cf5f03517)
- Bug 1241454: Unify AsmJSSimdOperation and SimdOperation; r=jolesen (7506dec0e4)
- Bug 1241454: Change SIMD opcodes encoding in wasm; r=luke, r=jolesen (afa2b74cab)
- Bug 1242804 - Baldr: check enum limits (r=bbouvier) (1a68daa1c3)
- Bug 1242804 - Baldr: add local/get_local/set_local (r=bbouvier) (cc2448d2e9)
- Bug 1243239 - Baldr: tighten signature index check (r=bbouvier) (fd7081ab39)
- Bug 1243373: Ensure all declared functions are defined; r=luke (3c42295668)
- Bug 1243031: Throw when WebAssembly is not supported; r=luke (ade7cdc6a6)
- Bug 1242772 - Baldr: fix unterminated string literal (r=bbouvier) (273e26e669)
- Bug 1241886: Fix debug assertion if we're in dead code; r=luke (59c009191b)
- Bug 1234985 - Odin: use VarU32 for integer literals (r=bbouvier) (62c7292462)
- Bug 1234985 - Odin: stop requiring explicit final return (r=bbouvier) (2a8bf1639e)
- Bug 1242949: Implement Block in WebAssembly; r=luke (e8997f2a2e)
- Bug 1242949: Add explicit keyword to WasmAstBlock ctor; r=bustage (239cdcf5d4)
- Bug 1243626 - Baldr: tweak block text format (r=bbouvier) (a6fe9248ce)
- Bug 1243252 - Baldr: refactor exports (r=bbouvier) (49ca6f519f)
- Bug 1243632 - Baldr: check TypeError vs. SyntaxError (r=bbouvier) (c70ab8e8ec)
- Bug 1243252 - Baldr: add import section (r=bbouvier) (16b98660c8)
- Bug 1243633 - Baldr: add call/call_import (r=bbouvier) (63dd480e7d)
- Bug 1234985 - Hoist CStringHasher (r=sfink) (c019cff4b3)
- Bug 1237445 - Implement GCRekeyableHashMap, r=terrence (c4acd75ad0)
- Bug 1244272 - BaldrMonkey: Implement support for i32.add. r=luke (ede8918dfe)
- Bug 1244272 - BaldrMonkey: Implement the rest of the i32 binary operators. r=luke (bf11a38b86)
- Bug 1244272 - BaldrMonkey: Implement the f32 and 64 operators. r=luke (2ea37d6a95)
- Bug 1244403 - Baldr: move kind out of WasmAstNode and into WasmAstExpr (r=bbouvier) (498fd0b4a5)
- Silence a GCC warning in JitCompartment.h. No bug#, rs=jandem on IRC. (a23ccc42b3)
- Bug 1237445 - Use GCHashMap for ICStubCodeMap, r=terrence (b1d970f3c9)
- Bug 1244403 - Baldr: remove unnecessary 'end' argument from (r=bbouvier) (8da28c587c)
- Bug 1244403 - Baldr: expose export func entry offsets directly (r=bbouvier) (c6f82070fb)
- Bug 1234862 - Part 2: Always use DefaultGCPolicy with GCVector; r=sfink (6d8cf44216)
- Bug 1237445 - Use GCHashTables for ObjectGroup sweeping, r=terrence (7fd47cc6fb)
- Bug 1237445 - Use GCHashMap for (CCW) WrapperMap, r=terrence (cf6788b6a4)
- Bug 1237447 - Disable the crashreporter when crashing intentionally in TestPLDHash. r=njn (d4744f47d4)
- Bug 1237445 - Rekey ArrayObjectTable, r=terrence (2ccfca974c)
- Bug 1234862 - Part 3: Always use DefaultGCPolicy for GCHashTables; r=sfink (d0f2472c1b)
- Bug 1234862 - Part 4: Always use DefaultGCPolicy for TraceableFifo; r=sfink (069e134534)
- Bug 1239494 - Use GCHashMap to simplify nsWrapperCache; r=jonco (0bb0069fc4)
- Bug 1234862 - Part 5: Rename DefaultGCPolicy to GCPolicy; r=sfink (cb93f0c133)
- Bug 1234862 - Part 5.1: Followup comment fixes; r=sfink (bd2930ddf2)
- Fix recent non-unified build bustage (no bug, rs=sunfish) (6c40eba8bc)
- Bug 1243815: Put hard limits to some variable lengths in wasm; r=luke (1df12aa08e)
- Bug 1244403 - Baldr: factor out DynamicLinkData (r=bbouvier) (f257b901a3)
- Bug 1244403 - Baldr: put import section first (r=bbouvier) (47e726c907)
- Bug 1244571 - BaldrMonkey: Implement the unary operators. r=luke (e22132ccb1)
- Bug 1244571 - BaldrMonkey: Implement the comparison operators. r=luke (aa6e7a71cd)
- Bug 1244571 - BaldrMonkey: Implement parsing, encoding, and decoding for the conversion operators. r=luke (73d65dc416)
- Bug 1244571 - BaldrMonkey: Type-check the unary and binary operators. r=luke (4d7d41d49b)
- Bug 1244571 - BaldrMonkey: Implement parsing, encoding, and decoding for i64 operators. r=luke (c42a9b5496)
- Bug 1244571 - BaldrMonkey: Improve the readability of the parser code. r=luke (298c9c57a5)
- Bug 1244571 - BaldrMonkey: Fail decoding for operators which are not yet implemented r=luke (fdd4deff21)
- Bug 1245250 - BaldrMonkey: Refacfor min/max to make variadicity AsmJS-specific r=luke (f30e0471b9)
- Bug 1242342: Add If and IfElse to WebAssembly; r=luke (653223fdd3)
- Bug 1247755 - Baldr: disallow duplicate signature table entries (r=sunfish) (419fd0be57)
- Bug 1234397 - dispatch on the correct value. r=luke (4c7d5f5105)
- Bug 1233863 - ARM64: Allow test to pass with --no-asmjs, and when no JIT exists. r=luke (b89522c52c)
- Bug 1244405 - Odin: switch to dynamic page size, move heap constants to Wasm (r=bbouvier) (31d4b684b7)
- Bug 1234038 - Self-host ArrayBuffer.prototype.slice. r=lth (ab81205e4a)
- Bug 1121937 - Implement %TypedArray%.prototype.sort; r=jorendorff (aef7bb1228)
- Bug 1101256 - Implement detachment checks for %TypedArray% methods (patch 1 of 2); r=Waldo (cb13a5078b)
- Bug 1101256 - Implement detachment checks for %TypedArray% methods (patch 2 of 2); r=Waldo (b429b87a3d)
- Bug 715181 - Self-host Array.sort; r=till (f87f85634c)
- Bug 1079844 - Adjust TypedArray.js comments to refer to detachment rather than neutering. r=mrrrgn (ac2b366958)
- Bug 1079844 - Rename scattered bits of 'neuter' terminology to detachment terminology. r=jandem (ae20a9677c)
- Bug 1079844 - Rename JS_ARRAYBUFFER_NEUTERED_FLAG to use detachment terminology. r=sfink (670281d8b0)
- Bug 1079844 - Rename isNeutered() to isDetached(), and rename isNeutered() on views to hasDetachedBuffer(). r=sfink (b5595746aa)
- Bug 1079844 - Convert the JS_NeuterArrayBufferObject API to detachment terminology. r=till (2d176589bd)
- Bug 1079844 - Rename JS_IsNeuteredArrayBufferObject to JS_IsDetachedArrayBufferObject. r=till (9607cf673d)
- Bug 1079844 - Refer to "detaching" instead of "neutering" of ArrayBuffers, in JIT optimization tracking code. r=jandem (a4a17233d0)
- Bug 1079844 - Rename TI's ObjectKey flag to use detachment terminology. r=jandem (8865017109)
- Bug 1079844 - Rename CheckForNeuteredTypedObject and the flag underlying it to use detachment terminology. r=jandem (67ace7669e)
- Bug 1079844 - Rename ABO::neuter to ABO::detach. r=sfink (37c71de755)
- Bug 1079844 - Rename ArrayBufferViewObject::neuter to ABVO::notifyBufferDetached. Detachment applies only to ArrayBuffers, and the methods didn't actually neuter anything, so "notify" makes more sense as the verb here. r=sfink (2ebf7779a7)
- Bug 1079844 - Rename ABO::neuterView to a standalone NoteViewBufferWasDetached function. r=sfink (3ce2977123)
- Bug 1244405 - Odin: refactor ArrayBufferObject::createForWasm out of prepareForAsmJS (r=bbouvier) (3f5ed523f0)
- Bug 1243633 - Odin: tidy up call emitting (r=bbouvier) (89256a6d57)
- Bug 1243633 - Odin: switch to lineOrBytecode from line/column (r=bbouvier) (ded5ff23de)
- Bug 1242342: Set return type accordingly to the compilation mode; r=luke (f4cdff1644)
- Bug 1244405 - Baldr: fix wasm function export name (r=bbouvier) (c6ea5f8194)
- Bug 1244405 - Baldr: add memory section (r=bbouvier) (c98fc66e50)
- Bug 1243252 - Remove const restriction on CharsToNewUTF8CharsZ (r=jandem) (9ef020aff6)
- fix neutered/detached (2a675ae62e)
- Bug 1242342: Replace Ternary by IfElse which return expressions; r=luke (aa1535de89)
- Bug 1244405 - Baldr: add memory exports (r=bbouvier) (9a495599df)
- Bug 1240583 - Odin: remove the sync interrupt stub (r=bbouvier) (a3811b0cc5)
- Bug 1242043 - {Array,%TypedArray%}.prototype.{i,lastI}ndexOf should never return -0. r=jorendorff (485f5d62dc)
- Bug 1180290 - Part 1: Add prefix parameter to IdToFunctionName. r=till (e33ed94f64)
- Bug 1180290 - Part 2: Handle prefix in DefinePropertyById. r=till,smaug (5c3333ef68)
- Bug 1180290 - Part 3: Use canonical name in native getter. r=till (213b6d1b7f)
- Bug 1180290 - Part 4: Add tests for builtin getter name. r=till (005f1e4da7)
- Bug 1180290 - Part 5: Remove getter/setter variant for ThrowInvalidThis message. r=bz (fa8224af50)
- Bug 1236548 - Allow JS_UNINITIALIZED_LEXICAL in jit::InvokeFunction. (r=Waldo) (33b1f88506)
- Bug 1212533 - Change the out-param of js/JS::Construct from MutableHandleValue to MutableHandleObject. r=arai, r=jorendorff (7910b24126)
- Bug 1233857 - Teach the JIT how to put individual elements' edges in the store buffer; r=jandem (7d0b35b028)
- Bug 1236548 - Followup: Make added test actually pass. (rs=Waldo) (0f1e6861b1)
- Bug 1216166 - Unify CustomEvent.cpp with the rest of the unified sources, r=smaug (eacd905246)
- Bug 1233831 - Part 0: JSAPI plumbing for nursery collection callbacks; r=terrence (1417ff456a)
- Bug 1233831 - Part 1: Call the callback on nursery collections; r=terrence (cf720fc8c9)
- Bug 1233831 - Part 2: Expose JS::gcreason::ExplainReason to embedders; r=terrence (5eaceb2a58)
- Bug 1233831 - Part 3: Install a callback to trace nursery collections; r=mccr8 (13bc8d19ff)
- Bug 1233831 - Part 4: Add metadata for the minor GC markers; r=vporof (cb42e1e42a)
- Bug 1233831 - Part 5: Test the minor GC markers; r=vporof (5f8ae59242)
- Bug 1218468 - Remove worker markers on each check in browser_timelineMarkers-02.js, r=me (33efc054c0)
- Bug 1233831 - Follow up: Expand sanitizeMarkers to filter out minor GC markers; r=jsantell (b99fb1dbbc)
- Bug 1239314 - Make marking validation a normal zeal mode; r=jonco (4668054d11)
- Bug 1233857 - Follow up: Add a new GC zeal mode for the elements edges barrier; r=terrence (61a7db4357)
- Bug 1245767 - Allow combining different gczeal modes. r=terrence (8d90fd103e)
- Bug 1242262 - Remove the last vestiges of JS_GC_MARKING_VALIDATION (it's a GC Zeal mode now). r=terrence (3ca086aed4)
- Bug 1249795 - Clear strong refs to message managers once we've destroyed nsFrameLoader, r=billm (b562395eb9)
- Bug 1244405 - Baldr: add memory segments (r=sunfish) (c7850ee495)
- Bug 1246433 - BaldrMonkey: Implement integer and float literals. r=luke (d668caa1bd)
- Bug 1240583 - Odin: refactor x86/x64 loads/stores (r=sunfish) (df85bc0aa3)
- Bug 1240583 - Odin: replace retargetWithOffset (r=bbouvier) (627815cb6d)
- Bug 757969 - use __thread in ThreadLocal; r=froydnj (5e06f7ef9e)
- bug 1244128 - stop exporting js friend api symbols from libxul r=glandium (894792316d)
- Bug 912337 - Make a new Error subclass: Debugger.DebuggeeWouldRun. (r=jimb) (a020dfd40c)
- Bug 1248101 - Make dtoa_malloc infallible. r=sfink. (0828c1b15b)
- Bug 1245737 (part 1) - Enable |Omit_Private_Memory| for our dtoa implementation. r=bhackett. (aaa0ebad66)
- Bug 1245737 (part 0) - Fix a leak in DtoaState. r=sfink. (3007242e86)
- Bug 1245737 (part 2) - Don't bother measuring DtoaState. r=bhackett. (db33501ddd)
- Bug 1243888 - Derive RootKind automatically from TraceKind; r=sfink (08421e1c6c)
- Bug 1244358 - Support all types in PersistentRooted; r=sfink (bc1a8ada00)
- Bug 1248233. Make js::ReportOutOfMemory respect the autoJSAPIOwnsErrorReporting() flag on the context options. r=luke (5ee05b61e6)
- Bug 1235151 - Factor all the install.rdfs used for langpacks. r=mshal (0e10679502)
- Bug 1235151 - Use #include in toolkit/locales/generic/install.rdf instead of -I on the preprocessor command line. r=mshal (31fdfea3de)
- Bug 1227388 - Finish removing dehydra support. r=mshal (aa43dfce95)
- Bug 1205814 - mozilla eslint plug: allow top level var only r=pbrosset (f247252d4b)
- Bug 1218409 - Eslint rule that checks for balanced listeners. r=miker (45d42a1014)
- Bug 1205814 - Missed a brief description in rst docs r=pbrosset (13fb24c5c4)
- Bug 1218425 - ESLint rule that warns against aArg notation in function params; r=miker (e811f2c8f7)
- Bug 1217851 - Fix mozplugin's import-headjs-globals on Windows. r=pbro (685f450fcb)
- Bug 1218412 - Create ESLint rule to check for CPOWS in browser mochitests r=pbrosset (c505ab4ed3)
- Bug 1217922 - eslint head.js plugin does not seem to work r=pbrosset (8d3782057e)
- Bug 1224735 - don't use "for..of" in eslint plugin. r=miker (90ecebec24)
- Bug 1225289 - Make eslint plugin code conform to .eslintrc r=pbrosset (007bf4c6b8)
- Bug 1222232 - Help mach eslint find espree and escope r=pbrosset (d0f7c5987b)
- Bug 1229858: Add a preprocessor to convert XBL into JavaScript blocks for eslint. r=miker (99bd8d62c4)
- Bug 1230300: Fix mach eslint to pass command arguments through to eslint. r=gps (7b54b30a8f)
- Bug 1231720 - Log when ESLint finishes running. r=nalexander (12705e260d)
- Bug 1234164 - Fix Ion GETNAME stubs to check for uninitialized lexicals. (r=efaust) (561d6e2d1c)
- Bug 1239519 - Fix debug-only initializion order issue detected by ASan. r=luke (8aa8b63fb6)
- Bug 1242840 - Drop profiler invalidation event if we hit OOM and make invalidation infallible r=jandem (8dfe4d99a5)
- Bug 1232386 - Fix NativeObject::growSlotsStatic to recover from OOM. r=terrence (284f47d6b1)
- Bug 1235032 - check value of obj, remake while () {} to do { } while(). r=jorendorff (0cb646a76e)
- Bug 1228327 - Fix bogus assert in GenerateDenseElementHole, add some comments. r=bhackett (e7fc0db6a0)
- Bug 1232859 - Don't crash or abort compilation if we're unable to compile the regex stubs. r=nbp (0ec48d44b5)
- Bug 1237566 - Bake in global this-value in IonBuilder::jsop_functionthis in some cases. r=h4writer (bd1fb0e8d8)
- Bug 1000780 - Part 7: Fix perf regressions introduced in part 1. r=jandem (629452aa16)
- Bug 1240100: IonMonkey: Magic values cannot flow through an instruction, r=jandem (f644caf377)
- Bug 1244098 - Attempt to fold JSOP_IN to false in IonBuilder based on TI. r=bhackett (e964e1d2bf)
- Bug 1238592 - IonMonkey: Optimize away any OSR fixup blocks that are ultimately unreachable. r=nbp (3e7ad0d61a)
- Bug 1245171 - Ensure enough ballast space in js::jit::AddKeepAliveInstructions. r=jandem (ba48c30163)
- Bug 1244828 - Ensure enough ballast space in TypeAnalyzer::adjustPhiInputs. r=h4writer (126c899284)
- Bug 1244828 - Ensure enough ballast space in TypeAnalyzer::adjustInputs. r=h4writer (0bf3d584b9)
- Bug 1112537 - Optimize String#split('foo').join('bar') pattern. r=nbp (75fb0e4124)
2023-08-27 23:02:30 +08:00

3085 lines
94 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Class for managing loading of a subframe (creation of the docshell,
* handling of loads in it, recursion-checking).
*/
#include "base/basictypes.h"
#include "prenv.h"
#include "mozIApplication.h"
#include "nsDocShell.h"
#include "nsIDOMHTMLIFrameElement.h"
#include "nsIDOMHTMLFrameElement.h"
#include "nsIDOMMozBrowserFrame.h"
#include "nsIDOMWindow.h"
#include "nsIPresShell.h"
#include "nsIContentInlines.h"
#include "nsIContentViewer.h"
#include "nsIDocument.h"
#include "nsIDOMDocument.h"
#include "nsPIDOMWindow.h"
#include "nsIWebNavigation.h"
#include "nsIWebProgress.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIDocShellLoadInfo.h"
#include "nsIBaseWindow.h"
#include "nsContentUtils.h"
#include "nsIXPConnect.h"
#include "nsUnicharUtils.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptSecurityManager.h"
#include "nsIScrollable.h"
#include "nsFrameLoader.h"
#include "nsIDOMEventTarget.h"
#include "nsIFrame.h"
#include "nsIScrollableFrame.h"
#include "nsSubDocumentFrame.h"
#include "nsError.h"
#include "nsISHistory.h"
#include "nsISHistoryInternal.h"
#include "nsIDOMHTMLDocument.h"
#include "nsIXULWindow.h"
#include "nsIEditor.h"
#include "nsIMozBrowserFrame.h"
#include "nsIPermissionManager.h"
#include "nsISHistory.h"
#include "nsNullPrincipal.h"
#include "nsIScriptError.h"
#include "nsGlobalWindow.h"
#include "nsPIWindowRoot.h"
#include "nsLayoutUtils.h"
#include "nsView.h"
#include "nsIURI.h"
#include "nsIURL.h"
#include "nsNetUtil.h"
#include "nsGkAtoms.h"
#include "nsNameSpaceManager.h"
#include "nsThreadUtils.h"
#include "nsIDOMChromeWindow.h"
#include "nsInProcessTabChildGlobal.h"
#include "Layers.h"
#include "ClientLayerManager.h"
#include "AppProcessChecker.h"
#include "ContentParent.h"
#include "TabParent.h"
#include "mozilla/plugins/PPluginWidgetParent.h"
#include "../plugins/ipc/PluginWidgetParent.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/Preferences.h"
#include "mozilla/unused.h"
#include "mozilla/dom/Element.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "mozilla/layout/RenderFrameParent.h"
#include "nsIAppsService.h"
#include "GeckoProfiler.h"
#include "jsapi.h"
#include "mozilla/dom/HTMLIFrameElement.h"
#include "nsSandboxFlags.h"
#include "mozilla/layers/CompositorChild.h"
#include "mozilla/dom/ipc/StructuredCloneData.h"
#include "nsPrincipal.h"
#ifdef MOZ_XUL
#include "nsXULPopupManager.h"
#endif
using namespace mozilla;
using namespace mozilla::hal;
using namespace mozilla::dom;
using namespace mozilla::dom::ipc;
using namespace mozilla::layers;
using namespace mozilla::layout;
typedef FrameMetrics::ViewID ViewID;
// Bug 136580: Limit to the number of nested content frames that can have the
// same URL. This is to stop content that is recursively loading
// itself. Note that "#foo" on the end of URL doesn't affect
// whether it's considered identical, but "?foo" or ";foo" are
// considered and compared.
// Bug 228829: Limit this to 1, like IE does.
#define MAX_SAME_URL_CONTENT_FRAMES 1
// Bug 8065: Limit content frame depth to some reasonable level. This
// does not count chrome frames when determining depth, nor does it
// prevent chrome recursion. Number is fairly arbitrary, but meant to
// keep number of shells to a reasonable number on accidental recursion with a
// small (but not 1) branching factor. With large branching factors the number
// of shells can rapidly become huge and run us out of memory. To solve that,
// we'd need to re-institute a fixed version of bug 98158.
#define MAX_DEPTH_CONTENT_FRAMES 10
NS_IMPL_CYCLE_COLLECTION(nsFrameLoader, mDocShell, mMessageManager, mChildMessageManager)
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader)
NS_INTERFACE_MAP_ENTRY(nsIFrameLoader)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFrameLoader)
NS_INTERFACE_MAP_END
nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated)
: mOwnerContent(aOwner)
, mAppIdSentToPermissionManager(nsIScriptSecurityManager::NO_APP_ID)
, mDetachedSubdocFrame(nullptr)
, mRemoteBrowser(nullptr)
, mChildID(0)
, mEventMode(EVENT_MODE_NORMAL_DISPATCH)
, mIsPrerendered(false)
, mDepthTooGreat(false)
, mIsTopLevelContent(false)
, mDestroyCalled(false)
, mNeedsAsyncDestroy(false)
, mInSwap(false)
, mInShow(false)
, mHideCalled(false)
, mNetworkCreated(aNetworkCreated)
, mRemoteBrowserShown(false)
, mRemoteFrame(false)
, mClipSubdocument(true)
, mClampScrollPosition(true)
, mObservingOwnerContent(false)
, mVisible(true)
{
mRemoteFrame = ShouldUseRemoteProcess();
}
nsFrameLoader::~nsFrameLoader()
{
if (mMessageManager) {
mMessageManager->Disconnect();
}
MOZ_RELEASE_ASSERT(mDestroyCalled);
}
nsFrameLoader*
nsFrameLoader::Create(Element* aOwner, bool aNetworkCreated)
{
NS_ENSURE_TRUE(aOwner, nullptr);
nsIDocument* doc = aOwner->OwnerDoc();
NS_ENSURE_TRUE(!doc->IsResourceDoc() &&
((!doc->IsLoadedAsData() && aOwner->GetComposedDoc()) ||
doc->IsStaticDocument()),
nullptr);
return new nsFrameLoader(aOwner, aNetworkCreated);
}
NS_IMETHODIMP
nsFrameLoader::LoadFrame()
{
NS_ENSURE_TRUE(mOwnerContent, NS_ERROR_NOT_INITIALIZED);
nsAutoString src;
bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc);
if (isSrcdoc) {
src.AssignLiteral("about:srcdoc");
}
else {
GetURL(src);
src.Trim(" \t\n\r");
if (src.IsEmpty()) {
// If the frame is a XUL element and has the attribute 'nodefaultsrc=true'
// then we will not use 'about:blank' as fallback but return early without
// starting a load if no 'src' attribute is given (or it's empty).
if (mOwnerContent->IsXULElement() &&
mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::nodefaultsrc,
nsGkAtoms::_true, eCaseMatters)) {
return NS_OK;
}
src.AssignLiteral("about:blank");
}
}
nsIDocument* doc = mOwnerContent->OwnerDoc();
if (doc->IsStaticDocument()) {
return NS_OK;
}
nsCOMPtr<nsIURI> base_uri = mOwnerContent->GetBaseURI();
const nsAFlatCString &doc_charset = doc->GetDocumentCharacterSet();
const char *charset = doc_charset.IsEmpty() ? nullptr : doc_charset.get();
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), src, charset, base_uri);
// If the URI was malformed, try to recover by loading about:blank.
if (rv == NS_ERROR_MALFORMED_URI) {
rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_STRING("about:blank"),
charset, base_uri);
}
if (NS_SUCCEEDED(rv)) {
rv = LoadURI(uri);
}
if (NS_FAILED(rv)) {
FireErrorEvent();
return rv;
}
return NS_OK;
}
void
nsFrameLoader::FireErrorEvent()
{
if (!mOwnerContent) {
return;
}
RefPtr<AsyncEventDispatcher > loadBlockingAsyncDispatcher =
new LoadBlockingAsyncEventDispatcher(mOwnerContent,
NS_LITERAL_STRING("error"),
false, false);
loadBlockingAsyncDispatcher->PostDOMEvent();
}
NS_IMETHODIMP
nsFrameLoader::LoadURI(nsIURI* aURI)
{
if (!aURI)
return NS_ERROR_INVALID_POINTER;
NS_ENSURE_STATE(!mDestroyCalled && mOwnerContent);
nsCOMPtr<nsIDocument> doc = mOwnerContent->OwnerDoc();
nsresult rv = CheckURILoad(aURI);
NS_ENSURE_SUCCESS(rv, rv);
mURIToLoad = aURI;
rv = doc->InitializeFrameLoader(this);
if (NS_FAILED(rv)) {
mURIToLoad = nullptr;
}
return rv;
}
NS_IMETHODIMP
nsFrameLoader::SwitchProcessAndLoadURI(nsIURI* aURI, const nsACString& aPackageId)
{
nsCOMPtr<nsIURI> URIToLoad = aURI;
RefPtr<TabParent> tp = nullptr;
nsCString signedPkgOrigin;
if (!aPackageId.IsEmpty()) {
// Only when aPackageId is not empty would signed package origin
// be meaningful.
nsPrincipal::GetOriginForURI(aURI, signedPkgOrigin);
}
MutableTabContext context;
nsresult rv = GetNewTabContext(&context, signedPkgOrigin, aPackageId);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<Element> ownerElement = mOwnerContent;
tp = ContentParent::CreateBrowserOrApp(context, ownerElement, nullptr);
if (!tp) {
return NS_ERROR_FAILURE;
}
mRemoteBrowserShown = false;
rv = SwapRemoteBrowser(tp);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
LoadURI(URIToLoad);
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::SetIsPrerendered()
{
MOZ_ASSERT(!mDocShell, "Please call SetIsPrerendered before docShell is created");
mIsPrerendered = true;
return NS_OK;
}
nsresult
nsFrameLoader::ReallyStartLoading()
{
nsresult rv = ReallyStartLoadingInternal();
if (NS_FAILED(rv)) {
FireErrorEvent();
}
return rv;
}
nsresult
nsFrameLoader::ReallyStartLoadingInternal()
{
NS_ENSURE_STATE(mURIToLoad && mOwnerContent && mOwnerContent->IsInComposedDoc());
PROFILER_LABEL("nsFrameLoader", "ReallyStartLoading",
js::ProfileEntry::Category::OTHER);
if (IsRemoteFrame()) {
if (!mRemoteBrowser && !TryRemoteBrowser()) {
NS_WARNING("Couldn't create child process for iframe.");
return NS_ERROR_FAILURE;
}
// FIXME get error codes from child
mRemoteBrowser->LoadURL(mURIToLoad);
if (!mRemoteBrowserShown && !ShowRemoteFrame(ScreenIntSize(0, 0))) {
NS_WARNING("[nsFrameLoader] ReallyStartLoadingInternal tried but couldn't show remote browser.\n");
}
return NS_OK;
}
nsresult rv = MaybeCreateDocShell();
if (NS_FAILED(rv)) {
return rv;
}
NS_ASSERTION(mDocShell,
"MaybeCreateDocShell succeeded with a null mDocShell");
// Just to be safe, recheck uri.
rv = CheckURILoad(mURIToLoad);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
mDocShell->CreateLoadInfo(getter_AddRefs(loadInfo));
NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
// If this frame is sandboxed with respect to origin we will set it up with
// a null principal later in nsDocShell::DoURILoad.
// We do it there to correctly sandbox content that was loaded into
// the frame via other methods than the src attribute.
// We'll use our principal, not that of the document loaded inside us. This
// is very important; needed to prevent XSS attacks on documents loaded in
// subframes!
loadInfo->SetOwner(mOwnerContent->NodePrincipal());
nsCOMPtr<nsIURI> referrer;
nsAutoString srcdoc;
bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::srcdoc,
srcdoc);
if (isSrcdoc) {
nsAutoString referrerStr;
mOwnerContent->OwnerDoc()->GetReferrer(referrerStr);
rv = NS_NewURI(getter_AddRefs(referrer), referrerStr);
loadInfo->SetSrcdocData(srcdoc);
nsCOMPtr<nsIURI> baseURI = mOwnerContent->GetBaseURI();
loadInfo->SetBaseURI(baseURI);
}
else {
rv = mOwnerContent->NodePrincipal()->GetURI(getter_AddRefs(referrer));
NS_ENSURE_SUCCESS(rv, rv);
}
// Use referrer as long as it is not an nsNullPrincipalURI.
// We could add a method such as GetReferrerURI to principals to make this
// cleaner, but given that we need to start using Source Browsing Context for
// referrer (see Bug 960639) this may be wasted effort at this stage.
if (referrer) {
bool isNullPrincipalScheme;
rv = referrer->SchemeIs(NS_NULLPRINCIPAL_SCHEME, &isNullPrincipalScheme);
if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) {
loadInfo->SetReferrer(referrer);
}
}
// get referrer policy for this iframe:
// first load document wide policy, then
// load iframe referrer attribute if enabled in preferences
// per element referrer overrules document wide referrer if enabled
net::ReferrerPolicy referrerPolicy = mOwnerContent->OwnerDoc()->GetReferrerPolicy();
HTMLIFrameElement* iframe = HTMLIFrameElement::FromContent(mOwnerContent);
if (iframe) {
net::ReferrerPolicy iframeReferrerPolicy = iframe->GetReferrerPolicyAsEnum();
if (iframeReferrerPolicy != net::RP_Unset) {
referrerPolicy = iframeReferrerPolicy;
}
}
loadInfo->SetReferrerPolicy(referrerPolicy);
// Default flags:
int32_t flags = nsIWebNavigation::LOAD_FLAGS_NONE;
// Flags for browser frame:
if (OwnerIsBrowserFrame()) {
flags = nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_OWNER;
}
// Kick off the load...
bool tmpState = mNeedsAsyncDestroy;
mNeedsAsyncDestroy = true;
nsCOMPtr<nsIURI> uriToLoad = mURIToLoad;
rv = mDocShell->LoadURI(uriToLoad, loadInfo, flags, false);
mNeedsAsyncDestroy = tmpState;
mURIToLoad = nullptr;
NS_ENSURE_SUCCESS(rv, rv);
// Track the appId's reference count if this frame is in-process
ResetPermissionManagerStatus();
return NS_OK;
}
nsresult
nsFrameLoader::CheckURILoad(nsIURI* aURI)
{
// Check for security. The fun part is trying to figure out what principals
// to use. The way I figure it, if we're doing a LoadFrame() accidentally
// (eg someone created a frame/iframe node, we're being parsed, XUL iframes
// are being reframed, etc.) then we definitely want to use the node
// principal of mOwnerContent for security checks. If, on the other hand,
// someone's setting the src on our owner content, or created it via script,
// or whatever, then they can clearly access it... and we should still use
// the principal of mOwnerContent. I don't think that leads to privilege
// escalation, and it's reasonably guaranteed to not lead to XSS issues
// (since caller can already access mOwnerContent in this case). So just use
// the principal of mOwnerContent no matter what. If script wants to run
// things with its own permissions, which differ from those of mOwnerContent
// (which means the script is privileged in some way) it should set
// window.location instead.
nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
// Get our principal
nsIPrincipal* principal = mOwnerContent->NodePrincipal();
// Check if we are allowed to load absURL
nsresult rv =
secMan->CheckLoadURIWithPrincipal(principal, aURI,
nsIScriptSecurityManager::STANDARD);
if (NS_FAILED(rv)) {
return rv; // We're not
}
// Bail out if this is an infinite recursion scenario
if (IsRemoteFrame()) {
return NS_OK;
}
return CheckForRecursiveLoad(aURI);
}
NS_IMETHODIMP
nsFrameLoader::GetDocShell(nsIDocShell **aDocShell)
{
*aDocShell = nullptr;
nsresult rv = NS_OK;
if (IsRemoteFrame()) {
return rv;
}
// If we have an owner, make sure we have a docshell and return
// that. If not, we're most likely in the middle of being torn down,
// then we just return null.
if (mOwnerContent) {
nsresult rv = MaybeCreateDocShell();
if (NS_FAILED(rv)) {
return rv;
}
NS_ASSERTION(mDocShell,
"MaybeCreateDocShell succeeded, but null mDocShell");
}
*aDocShell = mDocShell;
NS_IF_ADDREF(*aDocShell);
return rv;
}
static void
SetTreeOwnerAndChromeEventHandlerOnDocshellTree(nsIDocShellTreeItem* aItem,
nsIDocShellTreeOwner* aOwner,
EventTarget* aHandler)
{
NS_PRECONDITION(aItem, "Must have item");
aItem->SetTreeOwner(aOwner);
int32_t childCount = 0;
aItem->GetChildCount(&childCount);
for (int32_t i = 0; i < childCount; ++i) {
nsCOMPtr<nsIDocShellTreeItem> item;
aItem->GetChildAt(i, getter_AddRefs(item));
if (aHandler) {
nsCOMPtr<nsIDocShell> shell(do_QueryInterface(item));
shell->SetChromeEventHandler(aHandler);
}
SetTreeOwnerAndChromeEventHandlerOnDocshellTree(item, aOwner, aHandler);
}
}
/**
* Set the type of the treeitem and hook it up to the treeowner.
* @param aItem the treeitem we're working with
* @param aTreeOwner the relevant treeowner; might be null
* @param aParentType the nsIDocShellTreeItem::GetType of our parent docshell
* @param aParentNode if non-null, the docshell we should be added as a child to
*
* @return whether aItem is top-level content
*/
bool
nsFrameLoader::AddTreeItemToTreeOwner(nsIDocShellTreeItem* aItem,
nsIDocShellTreeOwner* aOwner,
int32_t aParentType,
nsIDocShell* aParentNode)
{
NS_PRECONDITION(aItem, "Must have docshell treeitem");
NS_PRECONDITION(mOwnerContent, "Must have owning content");
nsAutoString value;
bool isContent = false;
mOwnerContent->GetAttr(kNameSpaceID_None, TypeAttrName(), value);
// we accept "content" and "content-xxx" values.
// at time of writing, we expect "xxx" to be "primary" or "targetable", but
// someday it might be an integer expressing priority or something else.
isContent = value.LowerCaseEqualsLiteral("content") ||
StringBeginsWith(value, NS_LITERAL_STRING("content-"),
nsCaseInsensitiveStringComparator());
// Force mozbrowser frames to always be typeContent, even if the
// mozbrowser interfaces are disabled.
nsCOMPtr<nsIDOMMozBrowserFrame> mozbrowser =
do_QueryInterface(mOwnerContent);
if (mozbrowser) {
bool isMozbrowser = false;
mozbrowser->GetMozbrowser(&isMozbrowser);
isContent |= isMozbrowser;
}
if (isContent) {
// The web shell's type is content.
aItem->SetItemType(nsIDocShellTreeItem::typeContent);
} else {
// Inherit our type from our parent docshell. If it is
// chrome, we'll be chrome. If it is content, we'll be
// content.
aItem->SetItemType(aParentType);
}
// Now that we have our type set, add ourselves to the parent, as needed.
if (aParentNode) {
aParentNode->AddChild(aItem);
}
bool retval = false;
if (aParentType == nsIDocShellTreeItem::typeChrome && isContent) {
retval = true;
bool is_primary = value.LowerCaseEqualsLiteral("content-primary");
if (aOwner) {
bool is_targetable = is_primary ||
value.LowerCaseEqualsLiteral("content-targetable");
mOwnerContent->AddMutationObserver(this);
mObservingOwnerContent = true;
aOwner->ContentShellAdded(aItem, is_primary, is_targetable, value);
}
}
return retval;
}
static bool
AllDescendantsOfType(nsIDocShellTreeItem* aParentItem, int32_t aType)
{
int32_t childCount = 0;
aParentItem->GetChildCount(&childCount);
for (int32_t i = 0; i < childCount; ++i) {
nsCOMPtr<nsIDocShellTreeItem> kid;
aParentItem->GetChildAt(i, getter_AddRefs(kid));
if (kid->ItemType() != aType || !AllDescendantsOfType(kid, aType)) {
return false;
}
}
return true;
}
/**
* A class that automatically sets mInShow to false when it goes
* out of scope.
*/
class MOZ_RAII AutoResetInShow {
private:
nsFrameLoader* mFrameLoader;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
public:
explicit AutoResetInShow(nsFrameLoader* aFrameLoader MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mFrameLoader(aFrameLoader)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
~AutoResetInShow() { mFrameLoader->mInShow = false; }
};
bool
nsFrameLoader::Show(int32_t marginWidth, int32_t marginHeight,
int32_t scrollbarPrefX, int32_t scrollbarPrefY,
nsSubDocumentFrame* frame)
{
if (mInShow) {
return false;
}
// Reset mInShow if we exit early.
AutoResetInShow resetInShow(this);
mInShow = true;
ScreenIntSize size = frame->GetSubdocumentSize();
if (IsRemoteFrame()) {
return ShowRemoteFrame(size, frame);
}
nsresult rv = MaybeCreateDocShell();
if (NS_FAILED(rv)) {
return false;
}
NS_ASSERTION(mDocShell,
"MaybeCreateDocShell succeeded, but null mDocShell");
if (!mDocShell) {
return false;
}
mDocShell->SetMarginWidth(marginWidth);
mDocShell->SetMarginHeight(marginHeight);
nsCOMPtr<nsIScrollable> sc = do_QueryInterface(mDocShell);
if (sc) {
sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X,
scrollbarPrefX);
sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y,
scrollbarPrefY);
}
nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
if (presShell) {
// Ensure root scroll frame is reflowed in case scroll preferences or
// margins have changed
nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
if (rootScrollFrame) {
presShell->FrameNeedsReflow(rootScrollFrame, nsIPresShell::eResize,
NS_FRAME_IS_DIRTY);
}
return true;
}
nsView* view = frame->EnsureInnerView();
if (!view)
return false;
nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(mDocShell);
NS_ASSERTION(baseWindow, "Found a nsIDocShell that isn't a nsIBaseWindow.");
baseWindow->InitWindow(nullptr, view->GetWidget(), 0, 0,
size.width, size.height);
// This is kinda whacky, this "Create()" call doesn't really
// create anything, one starts to wonder why this was named
// "Create"...
baseWindow->Create();
baseWindow->SetVisibility(true);
NS_ENSURE_TRUE(mDocShell, false);
// Trigger editor re-initialization if midas is turned on in the
// sub-document. This shouldn't be necessary, but given the way our
// editor works, it is. See
// https://bugzilla.mozilla.org/show_bug.cgi?id=284245
presShell = mDocShell->GetPresShell();
if (presShell) {
nsCOMPtr<nsIDOMHTMLDocument> doc =
do_QueryInterface(presShell->GetDocument());
if (doc) {
nsAutoString designMode;
doc->GetDesignMode(designMode);
if (designMode.EqualsLiteral("on")) {
// Hold on to the editor object to let the document reattach to the
// same editor object, instead of creating a new one.
nsCOMPtr<nsIEditor> editor;
nsresult rv = mDocShell->GetEditor(getter_AddRefs(editor));
NS_ENSURE_SUCCESS(rv, false);
doc->SetDesignMode(NS_LITERAL_STRING("off"));
doc->SetDesignMode(NS_LITERAL_STRING("on"));
} else {
// Re-initialize the presentation for contenteditable documents
bool editable = false,
hasEditingSession = false;
mDocShell->GetEditable(&editable);
mDocShell->GetHasEditingSession(&hasEditingSession);
nsCOMPtr<nsIEditor> editor;
mDocShell->GetEditor(getter_AddRefs(editor));
if (editable && hasEditingSession && editor) {
editor->PostCreate();
}
}
}
}
mInShow = false;
if (mHideCalled) {
mHideCalled = false;
Hide();
return false;
}
return true;
}
void
nsFrameLoader::MarginsChanged(uint32_t aMarginWidth,
uint32_t aMarginHeight)
{
// We assume that the margins are always zero for remote frames.
if (IsRemoteFrame())
return;
// If there's no docshell, we're probably not up and running yet.
// nsFrameLoader::Show() will take care of setting the right
// margins.
if (!mDocShell)
return;
// Set the margins
mDocShell->SetMarginWidth(aMarginWidth);
mDocShell->SetMarginHeight(aMarginHeight);
// Trigger a restyle if there's a prescontext
// FIXME: This could do something much less expensive.
RefPtr<nsPresContext> presContext;
mDocShell->GetPresContext(getter_AddRefs(presContext));
if (presContext)
presContext->RebuildAllStyleData(nsChangeHint(0), eRestyle_Subtree);
}
bool
nsFrameLoader::ShowRemoteFrame(const ScreenIntSize& size,
nsSubDocumentFrame *aFrame)
{
NS_ASSERTION(IsRemoteFrame(), "ShowRemote only makes sense on remote frames.");
if (!mRemoteBrowser && !TryRemoteBrowser()) {
NS_ERROR("Couldn't create child process.");
return false;
}
// FIXME/bug 589337: Show()/Hide() is pretty expensive for
// cross-process layers; need to figure out what behavior we really
// want here. For now, hack.
if (!mRemoteBrowserShown) {
if (!mOwnerContent ||
!mOwnerContent->GetComposedDoc()) {
return false;
}
RefPtr<layers::LayerManager> layerManager =
nsContentUtils::LayerManagerForDocument(mOwnerContent->GetComposedDoc());
if (!layerManager) {
// This is just not going to work.
return false;
}
nsPIDOMWindow* win = mOwnerContent->OwnerDoc()->GetWindow();
bool parentIsActive = false;
if (win) {
nsCOMPtr<nsPIWindowRoot> windowRoot =
static_cast<nsGlobalWindow*>(win)->GetTopWindowRoot();
if (windowRoot) {
nsPIDOMWindow* topWin = windowRoot->GetWindow();
parentIsActive = topWin && topWin->IsActive();
}
}
mRemoteBrowser->Show(size, parentIsActive);
mRemoteBrowserShown = true;
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
"remote-browser-shown", nullptr);
}
} else {
nsIntRect dimensions;
NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), false);
// Don't show remote iframe if we are waiting for the completion of reflow.
if (!aFrame || !(aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
mRemoteBrowser->UpdateDimensions(dimensions, size);
}
}
return true;
}
void
nsFrameLoader::Hide()
{
if (mHideCalled) {
return;
}
if (mInShow) {
mHideCalled = true;
return;
}
if (!mDocShell)
return;
nsCOMPtr<nsIContentViewer> contentViewer;
mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
if (contentViewer)
contentViewer->SetSticky(false);
nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(mDocShell);
NS_ASSERTION(baseWin,
"Found an nsIDocShell which doesn't implement nsIBaseWindow.");
baseWin->SetVisibility(false);
baseWin->SetParentWidget(nullptr);
}
nsresult
nsFrameLoader::SwapWithOtherRemoteLoader(nsFrameLoader* aOther,
RefPtr<nsFrameLoader>& aFirstToSwap,
RefPtr<nsFrameLoader>& aSecondToSwap)
{
MOZ_ASSERT(NS_IsMainThread());
Element* ourContent = mOwnerContent;
Element* otherContent = aOther->mOwnerContent;
if (!ourContent || !otherContent) {
// Can't handle this
return NS_ERROR_NOT_IMPLEMENTED;
}
// Make sure there are no same-origin issues
bool equal;
nsresult rv =
ourContent->NodePrincipal()->Equals(otherContent->NodePrincipal(), &equal);
if (NS_FAILED(rv) || !equal) {
// Security problems loom. Just bail on it all
return NS_ERROR_DOM_SECURITY_ERR;
}
nsIDocument* ourDoc = ourContent->GetComposedDoc();
nsIDocument* otherDoc = otherContent->GetComposedDoc();
if (!ourDoc || !otherDoc) {
// Again, how odd, given that we had docshells
return NS_ERROR_NOT_IMPLEMENTED;
}
nsIPresShell* ourShell = ourDoc->GetShell();
nsIPresShell* otherShell = otherDoc->GetShell();
if (!ourShell || !otherShell) {
return NS_ERROR_NOT_IMPLEMENTED;
}
if (mInSwap || aOther->mInSwap) {
return NS_ERROR_NOT_IMPLEMENTED;
}
mInSwap = aOther->mInSwap = true;
nsIFrame* ourFrame = ourContent->GetPrimaryFrame();
nsIFrame* otherFrame = otherContent->GetPrimaryFrame();
if (!ourFrame || !otherFrame) {
mInSwap = aOther->mInSwap = false;
return NS_ERROR_NOT_IMPLEMENTED;
}
nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame);
if (!ourFrameFrame) {
mInSwap = aOther->mInSwap = false;
return NS_ERROR_NOT_IMPLEMENTED;
}
rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
if (NS_FAILED(rv)) {
mInSwap = aOther->mInSwap = false;
return rv;
}
mRemoteBrowser->SwapLayerTreeObservers(aOther->mRemoteBrowser);
nsCOMPtr<nsIBrowserDOMWindow> otherBrowserDOMWindow =
aOther->mRemoteBrowser->GetBrowserDOMWindow();
nsCOMPtr<nsIBrowserDOMWindow> browserDOMWindow =
mRemoteBrowser->GetBrowserDOMWindow();
if (!otherBrowserDOMWindow || !browserDOMWindow) {
return NS_ERROR_NOT_IMPLEMENTED;
}
aOther->mRemoteBrowser->SetBrowserDOMWindow(browserDOMWindow);
mRemoteBrowser->SetBrowserDOMWindow(otherBrowserDOMWindow);
// Native plugin windows used by this remote content need to be reparented.
nsPIDOMWindow* newWin = ourDoc->GetWindow();
if (newWin) {
RefPtr<nsIWidget> newParent = ((nsGlobalWindow*)newWin)->GetMainWidget();
const ManagedContainer<mozilla::plugins::PPluginWidgetParent>& plugins =
aOther->mRemoteBrowser->ManagedPPluginWidgetParent();
for (auto iter = plugins.ConstIter(); !iter.Done(); iter.Next()) {
static_cast<mozilla::plugins::PluginWidgetParent*>(iter.Get()->GetKey())->SetParent(newParent);
}
}
MaybeUpdatePrimaryTabParent(eTabParentRemoved);
aOther->MaybeUpdatePrimaryTabParent(eTabParentRemoved);
SetOwnerContent(otherContent);
aOther->SetOwnerContent(ourContent);
mRemoteBrowser->SetOwnerElement(otherContent);
aOther->mRemoteBrowser->SetOwnerElement(ourContent);
MaybeUpdatePrimaryTabParent(eTabParentChanged);
aOther->MaybeUpdatePrimaryTabParent(eTabParentChanged);
RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
// Swap and setup things in parent message managers.
if (mMessageManager) {
mMessageManager->SetCallback(aOther);
}
if (aOther->mMessageManager) {
aOther->mMessageManager->SetCallback(this);
}
mMessageManager.swap(aOther->mMessageManager);
aFirstToSwap.swap(aSecondToSwap);
ourFrameFrame->EndSwapDocShells(otherFrame);
ourShell->BackingScaleFactorChanged();
otherShell->BackingScaleFactorChanged();
ourDoc->FlushPendingNotifications(Flush_Layout);
otherDoc->FlushPendingNotifications(Flush_Layout);
mInSwap = aOther->mInSwap = false;
Unused << mRemoteBrowser->SendSwappedWithOtherRemoteLoader();
Unused << aOther->mRemoteBrowser->SendSwappedWithOtherRemoteLoader();
return NS_OK;
}
class MOZ_STACK_CLASS AutoResetInFrameSwap final
{
public:
AutoResetInFrameSwap(nsFrameLoader* aThisFrameLoader,
nsFrameLoader* aOtherFrameLoader,
nsDocShell* aThisDocShell,
nsDocShell* aOtherDocShell,
EventTarget* aThisEventTarget,
EventTarget* aOtherEventTarget
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mThisFrameLoader(aThisFrameLoader)
, mOtherFrameLoader(aOtherFrameLoader)
, mThisDocShell(aThisDocShell)
, mOtherDocShell(aOtherDocShell)
, mThisEventTarget(aThisEventTarget)
, mOtherEventTarget(aOtherEventTarget)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
mThisFrameLoader->mInSwap = true;
mOtherFrameLoader->mInSwap = true;
mThisDocShell->SetInFrameSwap(true);
mOtherDocShell->SetInFrameSwap(true);
// Fire pageshow events on still-loading pages, and then fire pagehide
// events. Note that we do NOT fire these in the normal way, but just fire
// them on the chrome event handlers.
nsContentUtils::FirePageShowEvent(mThisDocShell, mThisEventTarget, false);
nsContentUtils::FirePageShowEvent(mOtherDocShell, mOtherEventTarget, false);
nsContentUtils::FirePageHideEvent(mThisDocShell, mThisEventTarget);
nsContentUtils::FirePageHideEvent(mOtherDocShell, mOtherEventTarget);
}
~AutoResetInFrameSwap()
{
nsContentUtils::FirePageShowEvent(mThisDocShell, mThisEventTarget, true);
nsContentUtils::FirePageShowEvent(mOtherDocShell, mOtherEventTarget, true);
mThisFrameLoader->mInSwap = false;
mOtherFrameLoader->mInSwap = false;
mThisDocShell->SetInFrameSwap(false);
mOtherDocShell->SetInFrameSwap(false);
}
private:
RefPtr<nsFrameLoader> mThisFrameLoader;
RefPtr<nsFrameLoader> mOtherFrameLoader;
RefPtr<nsDocShell> mThisDocShell;
RefPtr<nsDocShell> mOtherDocShell;
nsCOMPtr<EventTarget> mThisEventTarget;
nsCOMPtr<EventTarget> mOtherEventTarget;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
nsresult
nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
RefPtr<nsFrameLoader>& aFirstToSwap,
RefPtr<nsFrameLoader>& aSecondToSwap)
{
NS_PRECONDITION((aFirstToSwap == this && aSecondToSwap == aOther) ||
(aFirstToSwap == aOther && aSecondToSwap == this),
"Swapping some sort of random loaders?");
NS_ENSURE_STATE(!mInShow && !aOther->mInShow);
if (IsRemoteFrame() && aOther->IsRemoteFrame()) {
return SwapWithOtherRemoteLoader(aOther, aFirstToSwap, aSecondToSwap);
}
if (IsRemoteFrame() || aOther->IsRemoteFrame()) {
NS_WARNING("Swapping remote and non-remote frames is not currently supported");
return NS_ERROR_NOT_IMPLEMENTED;
}
Element* ourContent = mOwnerContent;
Element* otherContent = aOther->mOwnerContent;
if (!ourContent || !otherContent) {
// Can't handle this
return NS_ERROR_NOT_IMPLEMENTED;
}
// Make sure there are no same-origin issues
bool equal;
nsresult rv =
ourContent->NodePrincipal()->Equals(otherContent->NodePrincipal(), &equal);
if (NS_FAILED(rv) || !equal) {
// Security problems loom. Just bail on it all
return NS_ERROR_DOM_SECURITY_ERR;
}
RefPtr<nsDocShell> ourDocshell = static_cast<nsDocShell*>(GetExistingDocShell());
RefPtr<nsDocShell> otherDocshell = static_cast<nsDocShell*>(aOther->GetExistingDocShell());
if (!ourDocshell || !otherDocshell) {
// How odd
return NS_ERROR_NOT_IMPLEMENTED;
}
// To avoid having to mess with session history, avoid swapping
// frameloaders that don't correspond to root same-type docshells,
// unless both roots have session history disabled.
nsCOMPtr<nsIDocShellTreeItem> ourRootTreeItem, otherRootTreeItem;
ourDocshell->GetSameTypeRootTreeItem(getter_AddRefs(ourRootTreeItem));
otherDocshell->GetSameTypeRootTreeItem(getter_AddRefs(otherRootTreeItem));
nsCOMPtr<nsIWebNavigation> ourRootWebnav =
do_QueryInterface(ourRootTreeItem);
nsCOMPtr<nsIWebNavigation> otherRootWebnav =
do_QueryInterface(otherRootTreeItem);
if (!ourRootWebnav || !otherRootWebnav) {
return NS_ERROR_NOT_IMPLEMENTED;
}
nsCOMPtr<nsISHistory> ourHistory;
nsCOMPtr<nsISHistory> otherHistory;
ourRootWebnav->GetSessionHistory(getter_AddRefs(ourHistory));
otherRootWebnav->GetSessionHistory(getter_AddRefs(otherHistory));
if ((ourRootTreeItem != ourDocshell || otherRootTreeItem != otherDocshell) &&
(ourHistory || otherHistory)) {
return NS_ERROR_NOT_IMPLEMENTED;
}
// Also make sure that the two docshells are the same type. Otherwise
// swapping is certainly not safe. If this needs to be changed then
// the code below needs to be audited as it assumes identical types.
int32_t ourType = ourDocshell->ItemType();
int32_t otherType = otherDocshell->ItemType();
if (ourType != otherType) {
return NS_ERROR_NOT_IMPLEMENTED;
}
// One more twist here. Setting up the right treeowners in a heterogeneous
// tree is a bit of a pain. So make sure that if ourType is not
// nsIDocShellTreeItem::typeContent then all of our descendants are the same
// type as us.
if (ourType != nsIDocShellTreeItem::typeContent &&
(!AllDescendantsOfType(ourDocshell, ourType) ||
!AllDescendantsOfType(otherDocshell, otherType))) {
return NS_ERROR_NOT_IMPLEMENTED;
}
// Save off the tree owners, frame elements, chrome event handlers, and
// docshell and document parents before doing anything else.
nsCOMPtr<nsIDocShellTreeOwner> ourOwner, otherOwner;
ourDocshell->GetTreeOwner(getter_AddRefs(ourOwner));
otherDocshell->GetTreeOwner(getter_AddRefs(otherOwner));
// Note: it's OK to have null treeowners.
nsCOMPtr<nsIDocShellTreeItem> ourParentItem, otherParentItem;
ourDocshell->GetParent(getter_AddRefs(ourParentItem));
otherDocshell->GetParent(getter_AddRefs(otherParentItem));
if (!ourParentItem || !otherParentItem) {
return NS_ERROR_NOT_IMPLEMENTED;
}
// Make sure our parents are the same type too
int32_t ourParentType = ourParentItem->ItemType();
int32_t otherParentType = otherParentItem->ItemType();
if (ourParentType != otherParentType) {
return NS_ERROR_NOT_IMPLEMENTED;
}
nsCOMPtr<nsPIDOMWindow> ourWindow = ourDocshell->GetWindow();
nsCOMPtr<nsPIDOMWindow> otherWindow = otherDocshell->GetWindow();
nsCOMPtr<Element> ourFrameElement =
ourWindow->GetFrameElementInternal();
nsCOMPtr<Element> otherFrameElement =
otherWindow->GetFrameElementInternal();
nsCOMPtr<EventTarget> ourChromeEventHandler =
do_QueryInterface(ourWindow->GetChromeEventHandler());
nsCOMPtr<EventTarget> otherChromeEventHandler =
do_QueryInterface(otherWindow->GetChromeEventHandler());
nsCOMPtr<EventTarget> ourEventTarget = ourWindow->GetParentTarget();
nsCOMPtr<EventTarget> otherEventTarget = otherWindow->GetParentTarget();
NS_ASSERTION(SameCOMIdentity(ourFrameElement, ourContent) &&
SameCOMIdentity(otherFrameElement, otherContent) &&
SameCOMIdentity(ourChromeEventHandler, ourContent) &&
SameCOMIdentity(otherChromeEventHandler, otherContent),
"How did that happen, exactly?");
nsCOMPtr<nsIDocument> ourChildDocument = ourWindow->GetExtantDoc();
nsCOMPtr<nsIDocument> otherChildDocument = otherWindow ->GetExtantDoc();
if (!ourChildDocument || !otherChildDocument) {
// This shouldn't be happening
return NS_ERROR_NOT_IMPLEMENTED;
}
nsCOMPtr<nsIDocument> ourParentDocument =
ourChildDocument->GetParentDocument();
nsCOMPtr<nsIDocument> otherParentDocument =
otherChildDocument->GetParentDocument();
// Make sure to swap docshells between the two frames.
nsIDocument* ourDoc = ourContent->GetComposedDoc();
nsIDocument* otherDoc = otherContent->GetComposedDoc();
if (!ourDoc || !otherDoc) {
// Again, how odd, given that we had docshells
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_ASSERTION(ourDoc == ourParentDocument, "Unexpected parent document");
NS_ASSERTION(otherDoc == otherParentDocument, "Unexpected parent document");
nsIPresShell* ourShell = ourDoc->GetShell();
nsIPresShell* otherShell = otherDoc->GetShell();
if (!ourShell || !otherShell) {
return NS_ERROR_NOT_IMPLEMENTED;
}
if (ourDocshell->GetIsBrowserElement() !=
otherDocshell->GetIsBrowserElement() ||
ourDocshell->GetIsApp() != otherDocshell->GetIsApp()) {
return NS_ERROR_NOT_IMPLEMENTED;
}
if (mInSwap || aOther->mInSwap) {
return NS_ERROR_NOT_IMPLEMENTED;
}
AutoResetInFrameSwap autoFrameSwap(this, aOther, ourDocshell, otherDocshell,
ourEventTarget, otherEventTarget);
nsIFrame* ourFrame = ourContent->GetPrimaryFrame();
nsIFrame* otherFrame = otherContent->GetPrimaryFrame();
if (!ourFrame || !otherFrame) {
return NS_ERROR_NOT_IMPLEMENTED;
}
nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame);
if (!ourFrameFrame) {
return NS_ERROR_NOT_IMPLEMENTED;
}
// OK. First begin to swap the docshells in the two nsIFrames
rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
if (NS_FAILED(rv)) {
return rv;
}
// Now move the docshells to the right docshell trees. Note that this
// resets their treeowners to null.
ourParentItem->RemoveChild(ourDocshell);
otherParentItem->RemoveChild(otherDocshell);
if (ourType == nsIDocShellTreeItem::typeContent) {
ourOwner->ContentShellRemoved(ourDocshell);
otherOwner->ContentShellRemoved(otherDocshell);
}
ourParentItem->AddChild(otherDocshell);
otherParentItem->AddChild(ourDocshell);
// Restore the correct chrome event handlers.
ourDocshell->SetChromeEventHandler(otherChromeEventHandler);
otherDocshell->SetChromeEventHandler(ourChromeEventHandler);
// Restore the correct treeowners
// (and also chrome event handlers for content frames only).
SetTreeOwnerAndChromeEventHandlerOnDocshellTree(ourDocshell, otherOwner,
ourType == nsIDocShellTreeItem::typeContent ? otherChromeEventHandler : nullptr);
SetTreeOwnerAndChromeEventHandlerOnDocshellTree(otherDocshell, ourOwner,
ourType == nsIDocShellTreeItem::typeContent ? ourChromeEventHandler : nullptr);
// Switch the owner content before we start calling AddTreeItemToTreeOwner.
// Note that we rely on this to deal with setting mObservingOwnerContent to
// false and calling RemoveMutationObserver as needed.
SetOwnerContent(otherContent);
aOther->SetOwnerContent(ourContent);
AddTreeItemToTreeOwner(ourDocshell, otherOwner, otherParentType, nullptr);
aOther->AddTreeItemToTreeOwner(otherDocshell, ourOwner, ourParentType,
nullptr);
// SetSubDocumentFor nulls out parent documents on the old child doc if a
// new non-null document is passed in, so just go ahead and remove both
// kids before reinserting in the parent subdoc maps, to avoid
// complications.
ourParentDocument->SetSubDocumentFor(ourContent, nullptr);
otherParentDocument->SetSubDocumentFor(otherContent, nullptr);
ourParentDocument->SetSubDocumentFor(ourContent, otherChildDocument);
otherParentDocument->SetSubDocumentFor(otherContent, ourChildDocument);
ourWindow->SetFrameElementInternal(otherFrameElement);
otherWindow->SetFrameElementInternal(ourFrameElement);
RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
// Swap pointers in child message managers.
if (mChildMessageManager) {
nsInProcessTabChildGlobal* tabChild =
static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get());
tabChild->SetOwner(otherContent);
tabChild->SetChromeMessageManager(otherMessageManager);
}
if (aOther->mChildMessageManager) {
nsInProcessTabChildGlobal* otherTabChild =
static_cast<nsInProcessTabChildGlobal*>(aOther->mChildMessageManager.get());
otherTabChild->SetOwner(ourContent);
otherTabChild->SetChromeMessageManager(ourMessageManager);
}
// Swap and setup things in parent message managers.
if (mMessageManager) {
mMessageManager->SetCallback(aOther);
}
if (aOther->mMessageManager) {
aOther->mMessageManager->SetCallback(this);
}
mMessageManager.swap(aOther->mMessageManager);
aFirstToSwap.swap(aSecondToSwap);
// Drop any cached content viewers in the two session histories.
nsCOMPtr<nsISHistoryInternal> ourInternalHistory =
do_QueryInterface(ourHistory);
nsCOMPtr<nsISHistoryInternal> otherInternalHistory =
do_QueryInterface(otherHistory);
if (ourInternalHistory) {
ourInternalHistory->EvictAllContentViewers();
}
if (otherInternalHistory) {
otherInternalHistory->EvictAllContentViewers();
}
NS_ASSERTION(ourFrame == ourContent->GetPrimaryFrame() &&
otherFrame == otherContent->GetPrimaryFrame(),
"changed primary frame");
ourFrameFrame->EndSwapDocShells(otherFrame);
// If the content being swapped came from windows on two screens with
// incompatible backing resolution (e.g. dragging a tab between windows on
// hi-dpi and low-dpi screens), it will have style data that is based on
// the wrong appUnitsPerDevPixel value. So we tell the PresShells that their
// backing scale factor may have changed. (Bug 822266)
ourShell->BackingScaleFactorChanged();
otherShell->BackingScaleFactorChanged();
ourParentDocument->FlushPendingNotifications(Flush_Layout);
otherParentDocument->FlushPendingNotifications(Flush_Layout);
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::Destroy()
{
StartDestroy();
return NS_OK;
}
class nsFrameLoaderDestroyRunnable : public nsRunnable
{
enum DestroyPhase
{
// See the implementation of Run for an explanation of these phases.
eDestroyDocShell,
eWaitForUnloadMessage,
eDestroyComplete
};
RefPtr<nsFrameLoader> mFrameLoader;
DestroyPhase mPhase;
public:
explicit nsFrameLoaderDestroyRunnable(nsFrameLoader* aFrameLoader)
: mFrameLoader(aFrameLoader), mPhase(eDestroyDocShell) {}
NS_IMETHODIMP Run() override;
};
void
nsFrameLoader::StartDestroy()
{
// nsFrameLoader::StartDestroy is called just before the frameloader is
// detached from the <browser> element. Destruction continues in phases via
// the nsFrameLoaderDestroyRunnable.
if (mDestroyCalled) {
return;
}
mDestroyCalled = true;
// After this point, we return an error when trying to send a message using
// the message manager on the frame.
if (mMessageManager) {
mMessageManager->Close();
}
// Retain references to the <browser> element and the frameloader in case we
// receive any messages from the message manager on the frame. These
// references are dropped in DestroyComplete.
if (mChildMessageManager || mRemoteBrowser) {
mOwnerContentStrong = mOwnerContent;
if (mRemoteBrowser) {
mRemoteBrowser->CacheFrameLoader(this);
}
if (mChildMessageManager) {
mChildMessageManager->CacheFrameLoader(this);
}
}
// If the TabParent has installed any event listeners on the window, this is
// its last chance to remove them while we're still in the document.
if (mRemoteBrowser) {
mRemoteBrowser->RemoveWindowListeners();
}
nsCOMPtr<nsIDocument> doc;
bool dynamicSubframeRemoval = false;
if (mOwnerContent) {
doc = mOwnerContent->OwnerDoc();
dynamicSubframeRemoval = !mIsTopLevelContent && !doc->InUnlinkOrDeletion();
doc->SetSubDocumentFor(mOwnerContent, nullptr);
MaybeUpdatePrimaryTabParent(eTabParentRemoved);
SetOwnerContent(nullptr);
}
// Seems like this is a dynamic frame removal.
if (dynamicSubframeRemoval) {
if (mDocShell) {
mDocShell->RemoveFromSessionHistory();
}
}
// Let the tree owner know we're gone.
if (mIsTopLevelContent) {
if (mDocShell) {
nsCOMPtr<nsIDocShellTreeItem> parentItem;
mDocShell->GetParent(getter_AddRefs(parentItem));
nsCOMPtr<nsIDocShellTreeOwner> owner = do_GetInterface(parentItem);
if (owner) {
owner->ContentShellRemoved(mDocShell);
}
}
}
// Let our window know that we are gone
if (mDocShell) {
nsCOMPtr<nsPIDOMWindow> win_private(mDocShell->GetWindow());
if (win_private) {
win_private->SetFrameElementInternal(nullptr);
}
}
nsCOMPtr<nsIRunnable> destroyRunnable = new nsFrameLoaderDestroyRunnable(this);
if (mNeedsAsyncDestroy || !doc ||
NS_FAILED(doc->FinalizeFrameLoader(this, destroyRunnable))) {
NS_DispatchToCurrentThread(destroyRunnable);
}
}
nsresult
nsFrameLoaderDestroyRunnable::Run()
{
switch (mPhase) {
case eDestroyDocShell:
mFrameLoader->DestroyDocShell();
// In the out-of-process case, TabParent will eventually call
// DestroyComplete once it receives a __delete__ message from the child. In
// the in-process case, we dispatch a series of runnables to ensure that
// DestroyComplete gets called at the right time. The frame loader is kept
// alive by mFrameLoader during this time.
if (mFrameLoader->mChildMessageManager) {
// When the docshell is destroyed, NotifyWindowIDDestroyed is called to
// asynchronously notify {outer,inner}-window-destroyed via a runnable. We
// don't want DestroyComplete to run until after those runnables have
// run. Since we're enqueueing ourselves after the window-destroyed
// runnables are enqueued, we're guaranteed to run after.
mPhase = eWaitForUnloadMessage;
NS_DispatchToCurrentThread(this);
}
break;
case eWaitForUnloadMessage:
// The *-window-destroyed observers have finished running at this
// point. However, it's possible that a *-window-destroyed observer might
// have sent a message using the message manager. These messages might not
// have been processed yet. So we enqueue ourselves again to ensure that
// DestroyComplete runs after all messages sent by *-window-destroyed
// observers have been processed.
mPhase = eDestroyComplete;
NS_DispatchToCurrentThread(this);
break;
case eDestroyComplete:
// Now that all messages sent by unload listeners and window destroyed
// observers have been processed, we disconnect the message manager and
// finish destruction.
mFrameLoader->DestroyComplete();
break;
}
return NS_OK;
}
void
nsFrameLoader::DestroyDocShell()
{
// This code runs after the frameloader has been detached from the <browser>
// element. We postpone this work because we may not be allowed to run
// script at that time.
// Ask the TabChild to fire the frame script "unload" event, destroy its
// docshell, and finally destroy the PBrowser actor. This eventually leads to
// nsFrameLoader::DestroyComplete being called.
if (mRemoteBrowser) {
mRemoteBrowser->Destroy();
}
// Fire the "unload" event if we're in-process.
if (mChildMessageManager) {
static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->FireUnloadEvent();
}
// Destroy the docshell.
nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell));
if (base_win) {
base_win->Destroy();
}
mDocShell = nullptr;
if (mChildMessageManager) {
// Stop handling events in the in-process frame script.
static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->DisconnectEventListeners();
}
}
void
nsFrameLoader::DestroyComplete()
{
// We get here, as part of StartDestroy, after the docshell has been destroyed
// and all message manager messages sent during docshell destruction have been
// dispatched. We also get here if the child process crashes. In the latter
// case, StartDestroy might not have been called.
// Drop the strong references created in StartDestroy.
if (mChildMessageManager || mRemoteBrowser) {
mOwnerContentStrong = nullptr;
if (mRemoteBrowser) {
mRemoteBrowser->CacheFrameLoader(nullptr);
}
if (mChildMessageManager) {
mChildMessageManager->CacheFrameLoader(nullptr);
}
}
// Call TabParent::Destroy if we haven't already (in case of a crash).
if (mRemoteBrowser) {
mRemoteBrowser->SetOwnerElement(nullptr);
mRemoteBrowser->Destroy();
mRemoteBrowser = nullptr;
}
if (mMessageManager) {
mMessageManager->Disconnect();
}
if (mChildMessageManager) {
static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->Disconnect();
}
mMessageManager = nullptr;
mChildMessageManager = nullptr;
}
NS_IMETHODIMP
nsFrameLoader::GetDepthTooGreat(bool* aDepthTooGreat)
{
*aDepthTooGreat = mDepthTooGreat;
return NS_OK;
}
void
nsFrameLoader::SetOwnerContent(Element* aContent)
{
if (mObservingOwnerContent) {
mObservingOwnerContent = false;
mOwnerContent->RemoveMutationObserver(this);
}
mOwnerContent = aContent;
if (RenderFrameParent* rfp = GetCurrentRenderFrame()) {
rfp->OwnerContentChanged(aContent);
}
ResetPermissionManagerStatus();
}
bool
nsFrameLoader::OwnerIsBrowserOrAppFrame()
{
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
return browserFrame ? browserFrame->GetReallyIsBrowserOrApp() : false;
}
// The xpcom getter version
NS_IMETHODIMP
nsFrameLoader::GetOwnerIsBrowserOrAppFrame(bool* aResult)
{
*aResult = OwnerIsBrowserOrAppFrame();
return NS_OK;
}
bool
nsFrameLoader::OwnerIsWidget()
{
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
return browserFrame ? browserFrame->GetReallyIsWidget() : false;
}
// The xpcom getter version
NS_IMETHODIMP
nsFrameLoader::GetOwnerIsWidget(bool* aResult)
{
*aResult = OwnerIsWidget();
return NS_OK;
}
bool
nsFrameLoader::OwnerIsAppFrame()
{
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
return browserFrame ? browserFrame->GetReallyIsApp() : false;
}
bool
nsFrameLoader::OwnerIsBrowserFrame()
{
return OwnerIsBrowserOrAppFrame() && !OwnerIsAppFrame();
}
void
nsFrameLoader::GetOwnerAppManifestURL(nsAString& aOut)
{
aOut.Truncate();
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
if (browserFrame) {
browserFrame->GetAppManifestURL(aOut);
}
}
already_AddRefed<mozIApplication>
nsFrameLoader::GetOwnApp()
{
nsAutoString manifest;
GetOwnerAppManifestURL(manifest);
if (manifest.IsEmpty()) {
return nullptr;
}
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(appsService, nullptr);
nsCOMPtr<mozIApplication> app;
appsService->GetAppByManifestURL(manifest, getter_AddRefs(app));
return app.forget();
}
already_AddRefed<mozIApplication>
nsFrameLoader::GetContainingApp()
{
// See if our owner content's principal has an associated app.
uint32_t appId = mOwnerContent->NodePrincipal()->GetAppId();
MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
if (appId == nsIScriptSecurityManager::NO_APP_ID ||
appId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
return nullptr;
}
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(appsService, nullptr);
nsCOMPtr<mozIApplication> app;
appsService->GetAppByLocalId(appId, getter_AddRefs(app));
return app.forget();
}
bool
nsFrameLoader::ShouldUseRemoteProcess()
{
if (PR_GetEnv("MOZ_DISABLE_OOP_TABS") ||
Preferences::GetBool("dom.ipc.tabs.disabled", false)) {
return false;
}
// Don't try to launch nested children if we don't have OMTC.
// They won't render!
if (XRE_IsContentProcess() &&
!CompositorChild::ChildProcessHasCompositor()) {
return false;
}
if (XRE_IsContentProcess() &&
!(PR_GetEnv("MOZ_NESTED_OOP_TABS") ||
Preferences::GetBool("dom.ipc.tabs.nested.enabled", false))) {
return false;
}
// If we're an <iframe mozbrowser> and we don't have a "remote" attribute,
// fall back to the default.
if (OwnerIsBrowserOrAppFrame() &&
!mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::Remote)) {
return Preferences::GetBool("dom.ipc.browser_frames.oop_by_default", false);
}
// Otherwise, we're remote if we have "remote=true" and we're either a
// browser frame or a XUL element.
return (OwnerIsBrowserOrAppFrame() ||
mOwnerContent->GetNameSpaceID() == kNameSpaceID_XUL) &&
mOwnerContent->AttrValueIs(kNameSpaceID_None,
nsGkAtoms::Remote,
nsGkAtoms::_true,
eCaseMatters);
}
bool
nsFrameLoader::IsRemoteFrame()
{
if (mRemoteFrame) {
MOZ_ASSERT(!mDocShell, "Found a remote frame with a DocShell");
return true;
}
return false;
}
nsresult
nsFrameLoader::MaybeCreateDocShell()
{
if (mDocShell) {
return NS_OK;
}
if (IsRemoteFrame()) {
return NS_OK;
}
NS_ENSURE_STATE(!mDestroyCalled);
// Get our parent docshell off the document of mOwnerContent
// XXXbz this is such a total hack.... We really need to have a
// better setup for doing this.
nsIDocument* doc = mOwnerContent->OwnerDoc();
if (!(doc->IsStaticDocument() || mOwnerContent->IsInComposedDoc())) {
return NS_ERROR_UNEXPECTED;
}
if (doc->IsResourceDoc() || !doc->IsActive()) {
// Don't allow subframe loads in resource documents, nor
// in non-active documents.
return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<nsIDocShell> docShell = doc->GetDocShell();
nsCOMPtr<nsIWebNavigation> parentAsWebNav = do_QueryInterface(docShell);
NS_ENSURE_STATE(parentAsWebNav);
// Create the docshell...
mDocShell = do_CreateInstance("@mozilla.org/docshell;1");
NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
if (mIsPrerendered) {
nsresult rv = mDocShell->SetIsPrerendered(true);
NS_ENSURE_SUCCESS(rv,rv);
}
// Apply sandbox flags even if our owner is not an iframe, as this copies
// flags from our owning content's owning document.
uint32_t sandboxFlags = 0;
HTMLIFrameElement* iframe = HTMLIFrameElement::FromContent(mOwnerContent);
if (iframe) {
sandboxFlags = iframe->GetSandboxFlags();
}
ApplySandboxFlags(sandboxFlags);
if (!mNetworkCreated) {
if (mDocShell) {
mDocShell->SetCreatedDynamically(true);
}
}
// Get the frame name and tell the docshell about it.
NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
nsAutoString frameName;
int32_t namespaceID = mOwnerContent->GetNameSpaceID();
if (namespaceID == kNameSpaceID_XHTML && !mOwnerContent->IsInHTMLDocument()) {
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, frameName);
} else {
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, frameName);
// XXX if no NAME then use ID, after a transition period this will be
// changed so that XUL only uses ID too (bug 254284).
if (frameName.IsEmpty() && namespaceID == kNameSpaceID_XUL) {
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, frameName);
}
}
if (!frameName.IsEmpty()) {
mDocShell->SetName(frameName);
}
//Grab the userContextId from owner if XUL
nsAutoString userContextIdStr;
if (namespaceID == kNameSpaceID_XUL) {
if (mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usercontextid)) {
mOwnerContent->GetAttr(kNameSpaceID_None,
nsGkAtoms::usercontextid,
userContextIdStr);
}
}
if (!userContextIdStr.IsEmpty()) {
nsresult err;
nsDocShell * ds = nsDocShell::Cast(mDocShell);
ds->SetUserContextId(userContextIdStr.ToInteger(&err));
NS_ENSURE_SUCCESS(err, err);
}
// Inform our docShell that it has a new child.
// Note: This logic duplicates a lot of logic in
// nsSubDocumentFrame::AttributeChanged. We should fix that.
int32_t parentType = docShell->ItemType();
// XXXbz why is this in content code, exactly? We should handle
// this some other way..... Not sure how yet.
nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
docShell->GetTreeOwner(getter_AddRefs(parentTreeOwner));
NS_ENSURE_STATE(parentTreeOwner);
mIsTopLevelContent =
AddTreeItemToTreeOwner(mDocShell, parentTreeOwner, parentType, docShell);
// Make sure all shells have links back to the content element
// in the nearest enclosing chrome shell.
nsCOMPtr<nsIDOMEventTarget> chromeEventHandler;
if (parentType == nsIDocShellTreeItem::typeChrome) {
// Our parent shell is a chrome shell. It is therefore our nearest
// enclosing chrome shell.
chromeEventHandler = do_QueryInterface(mOwnerContent);
NS_ASSERTION(chromeEventHandler,
"This mContent should implement this.");
} else {
// Our parent shell is a content shell. Get the chrome event
// handler from it and use that for our shell as well.
docShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler));
}
mDocShell->SetChromeEventHandler(chromeEventHandler);
// This is nasty, this code (the mDocShell->GetWindow() below)
// *must* come *after* the above call to
// mDocShell->SetChromeEventHandler() for the global window to get
// the right chrome event handler.
// Tell the window about the frame that hosts it.
nsCOMPtr<Element> frame_element = mOwnerContent;
NS_ASSERTION(frame_element, "frame loader owner element not a DOM element!");
nsCOMPtr<nsPIDOMWindow> win_private(mDocShell->GetWindow());
nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell));
if (win_private) {
win_private->SetFrameElementInternal(frame_element);
}
// This is kinda whacky, this call doesn't really create anything,
// but it must be called to make sure things are properly
// initialized.
if (NS_FAILED(base_win->Create()) || !win_private) {
// Do not call Destroy() here. See bug 472312.
NS_WARNING("Something wrong when creating the docshell for a frameloader!");
return NS_ERROR_FAILURE;
}
if (mIsTopLevelContent &&
mOwnerContent->IsXULElement(nsGkAtoms::browser) &&
!mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory)) {
nsresult rv;
nsCOMPtr<nsISHistory> sessionHistory =
do_CreateInstance(NS_SHISTORY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell));
webNav->SetSessionHistory(sessionHistory);
}
if (OwnerIsAppFrame()) {
// You can't be both an app and a browser frame.
MOZ_ASSERT(!OwnerIsBrowserFrame());
nsCOMPtr<mozIApplication> ownApp = GetOwnApp();
MOZ_ASSERT(ownApp);
uint32_t ownAppId = nsIScriptSecurityManager::NO_APP_ID;
if (ownApp) {
NS_ENSURE_SUCCESS(ownApp->GetLocalId(&ownAppId), NS_ERROR_FAILURE);
}
mDocShell->SetIsApp(ownAppId);
}
if (OwnerIsBrowserFrame()) {
// You can't be both a browser and an app frame.
MOZ_ASSERT(!OwnerIsAppFrame());
nsCOMPtr<mozIApplication> containingApp = GetContainingApp();
uint32_t containingAppId = nsIScriptSecurityManager::NO_APP_ID;
if (containingApp) {
NS_ENSURE_SUCCESS(containingApp->GetLocalId(&containingAppId),
NS_ERROR_FAILURE);
}
mDocShell->SetIsBrowserInsideApp(containingAppId);
}
if (OwnerIsBrowserOrAppFrame()) {
// For inproc frames, set the docshell properties.
nsCOMPtr<nsIDocShellTreeItem> item = do_GetInterface(docShell);
nsAutoString name;
if (mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) {
item->SetName(name);
}
mDocShell->SetFullscreenAllowed(
mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen));
bool isPrivate = mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing);
if (isPrivate) {
bool nonBlank;
mDocShell->GetHasLoadedNonBlankURI(&nonBlank);
if (nonBlank) {
nsContentUtils::ReportToConsoleNonLocalized(
NS_LITERAL_STRING("We should not switch to Private Browsing after loading a document."),
nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("mozprivatebrowsing"),
nullptr);
} else {
nsCOMPtr<nsILoadContext> context = do_GetInterface(mDocShell);
context->SetUsePrivateBrowsing(true);
}
}
}
ReallyLoadFrameScripts();
InitializeBrowserAPI();
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
"inprocess-browser-shown", nullptr);
}
return NS_OK;
}
void
nsFrameLoader::GetURL(nsString& aURI)
{
aURI.Truncate();
if (mOwnerContent->IsHTMLElement(nsGkAtoms::object)) {
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::data, aURI);
} else {
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, aURI);
}
}
nsresult
nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI)
{
nsresult rv;
MOZ_ASSERT(!IsRemoteFrame(),
"Shouldn't call CheckForRecursiveLoad on remote frames.");
mDepthTooGreat = false;
rv = MaybeCreateDocShell();
if (NS_FAILED(rv)) {
return rv;
}
NS_ASSERTION(mDocShell,
"MaybeCreateDocShell succeeded, but null mDocShell");
if (!mDocShell) {
return NS_ERROR_FAILURE;
}
// Check that we're still in the docshell tree.
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
NS_WARN_IF_FALSE(treeOwner,
"Trying to load a new url to a docshell without owner!");
NS_ENSURE_STATE(treeOwner);
if (mDocShell->ItemType() != nsIDocShellTreeItem::typeContent) {
// No need to do recursion-protection here XXXbz why not?? Do we really
// trust people not to screw up with non-content docshells?
return NS_OK;
}
// Bug 8065: Don't exceed some maximum depth in content frames
// (MAX_DEPTH_CONTENT_FRAMES)
nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
mDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem));
int32_t depth = 0;
while (parentAsItem) {
++depth;
if (depth >= MAX_DEPTH_CONTENT_FRAMES) {
mDepthTooGreat = true;
NS_WARNING("Too many nested content frames so giving up");
return NS_ERROR_UNEXPECTED; // Too deep, give up! (silently?)
}
nsCOMPtr<nsIDocShellTreeItem> temp;
temp.swap(parentAsItem);
temp->GetSameTypeParent(getter_AddRefs(parentAsItem));
}
// Bug 136580: Check for recursive frame loading excluding about:srcdoc URIs.
// srcdoc URIs require their contents to be specified inline, so it isn't
// possible for undesirable recursion to occur without the aid of a
// non-srcdoc URI, which this method will block normally.
// Besides, URI is not enough to guarantee uniqueness of srcdoc documents.
nsAutoCString buffer;
rv = aURI->GetScheme(buffer);
if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("about")) {
rv = aURI->GetPath(buffer);
if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("srcdoc")) {
// Duplicates allowed up to depth limits
return NS_OK;
}
}
int32_t matchCount = 0;
mDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem));
while (parentAsItem) {
// Check the parent URI with the URI we're loading
nsCOMPtr<nsIWebNavigation> parentAsNav(do_QueryInterface(parentAsItem));
if (parentAsNav) {
// Does the URI match the one we're about to load?
nsCOMPtr<nsIURI> parentURI;
parentAsNav->GetCurrentURI(getter_AddRefs(parentURI));
if (parentURI) {
// Bug 98158/193011: We need to ignore data after the #
bool equal;
rv = aURI->EqualsExceptRef(parentURI, &equal);
NS_ENSURE_SUCCESS(rv, rv);
if (equal) {
matchCount++;
if (matchCount >= MAX_SAME_URL_CONTENT_FRAMES) {
NS_WARNING("Too many nested content frames have the same url (recursion?) so giving up");
return NS_ERROR_UNEXPECTED;
}
}
}
}
nsCOMPtr<nsIDocShellTreeItem> temp;
temp.swap(parentAsItem);
temp->GetSameTypeParent(getter_AddRefs(parentAsItem));
}
return NS_OK;
}
nsresult
nsFrameLoader::GetWindowDimensions(nsIntRect& aRect)
{
if (!mOwnerContent) {
return NS_ERROR_FAILURE;
}
// Need to get outer window position here
nsIDocument* doc = mOwnerContent->GetComposedDoc();
if (!doc) {
return NS_ERROR_FAILURE;
}
if (doc->IsResourceDoc()) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsPIDOMWindow> win = doc->GetWindow();
if (!win) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDocShellTreeItem> parentAsItem(win->GetDocShell());
if (!parentAsItem) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDocShellTreeOwner> parentOwner;
if (NS_FAILED(parentAsItem->GetTreeOwner(getter_AddRefs(parentOwner))) ||
!parentOwner) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_GetInterface(parentOwner));
treeOwnerAsWin->GetPosition(&aRect.x, &aRect.y);
treeOwnerAsWin->GetSize(&aRect.width, &aRect.height);
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::UpdatePositionAndSize(nsSubDocumentFrame *aIFrame)
{
if (IsRemoteFrame()) {
if (mRemoteBrowser) {
ScreenIntSize size = aIFrame->GetSubdocumentSize();
nsIntRect dimensions;
NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), NS_ERROR_FAILURE);
mRemoteBrowser->UpdateDimensions(dimensions, size);
}
return NS_OK;
}
UpdateBaseWindowPositionAndSize(aIFrame);
return NS_OK;
}
void
nsFrameLoader::UpdateBaseWindowPositionAndSize(nsSubDocumentFrame *aIFrame)
{
nsCOMPtr<nsIDocShell> docShell;
GetDocShell(getter_AddRefs(docShell));
nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(docShell));
// resize the sub document
if (baseWindow) {
int32_t x = 0;
int32_t y = 0;
nsWeakFrame weakFrame(aIFrame);
baseWindow->GetPosition(&x, &y);
if (!weakFrame.IsAlive()) {
// GetPosition() killed us
return;
}
ScreenIntSize size = aIFrame->GetSubdocumentSize();
baseWindow->SetPositionAndSize(x, y, size.width, size.height, false);
}
}
NS_IMETHODIMP
nsFrameLoader::GetEventMode(uint32_t* aEventMode)
{
*aEventMode = mEventMode;
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::SetEventMode(uint32_t aEventMode)
{
mEventMode = aEventMode;
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::GetClipSubdocument(bool* aResult)
{
*aResult = mClipSubdocument;
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::SetClipSubdocument(bool aClip)
{
mClipSubdocument = aClip;
nsIFrame* frame = GetPrimaryFrameOfOwningContent();
if (frame) {
frame->InvalidateFrame();
frame->PresContext()->PresShell()->
FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
nsSubDocumentFrame* subdocFrame = do_QueryFrame(frame);
if (subdocFrame) {
nsIFrame* subdocRootFrame = subdocFrame->GetSubdocumentRootFrame();
if (subdocRootFrame) {
nsIFrame* subdocRootScrollFrame = subdocRootFrame->PresContext()->PresShell()->
GetRootScrollFrame();
if (subdocRootScrollFrame) {
frame->PresContext()->PresShell()->
FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
}
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::GetClampScrollPosition(bool* aResult)
{
*aResult = mClampScrollPosition;
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::SetClampScrollPosition(bool aClamp)
{
mClampScrollPosition = aClamp;
// When turning clamping on, make sure the current position is clamped.
if (aClamp) {
nsIFrame* frame = GetPrimaryFrameOfOwningContent();
nsSubDocumentFrame* subdocFrame = do_QueryFrame(frame);
if (subdocFrame) {
nsIFrame* subdocRootFrame = subdocFrame->GetSubdocumentRootFrame();
if (subdocRootFrame) {
nsIScrollableFrame* subdocRootScrollFrame = subdocRootFrame->PresContext()->PresShell()->
GetRootScrollFrameAsScrollable();
if (subdocRootScrollFrame) {
subdocRootScrollFrame->ScrollTo(subdocRootScrollFrame->GetScrollPosition(), nsIScrollableFrame::INSTANT);
}
}
}
}
return NS_OK;
}
bool
nsFrameLoader::TryRemoteBrowser()
{
NS_ASSERTION(!mRemoteBrowser, "TryRemoteBrowser called with a remote browser already?");
//XXXsmaug Per spec (2014/08/21) frameloader should not work in case the
// element isn't in document, only in shadow dom, but that will change
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=26365#c0
nsIDocument* doc = mOwnerContent->GetComposedDoc();
if (!doc) {
return false;
}
if (doc->IsResourceDoc()) {
// Don't allow subframe loads in external reference documents
return false;
}
nsCOMPtr<nsPIDOMWindow> parentWin = doc->GetWindow();
if (!parentWin) {
return false;
}
nsCOMPtr<nsIDocShell> parentDocShell = parentWin->GetDocShell();
if (!parentDocShell) {
return false;
}
TabParent* openingTab = TabParent::GetFrom(parentDocShell->GetOpener());
ContentParent* openerContentParent = nullptr;
if (openingTab &&
openingTab->Manager() &&
openingTab->Manager()->IsContentParent()) {
openerContentParent = openingTab->Manager()->AsContentParent();
}
// <iframe mozbrowser> gets to skip these checks.
if (!OwnerIsBrowserOrAppFrame()) {
if (parentDocShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
return false;
}
if (!mOwnerContent->IsXULElement()) {
return false;
}
nsAutoString value;
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type, value);
if (!value.LowerCaseEqualsLiteral("content") &&
!StringBeginsWith(value, NS_LITERAL_STRING("content-"),
nsCaseInsensitiveStringComparator())) {
return false;
}
}
uint32_t chromeFlags = 0;
nsCOMPtr<nsIDocShellTreeOwner> parentOwner;
if (NS_FAILED(parentDocShell->GetTreeOwner(getter_AddRefs(parentOwner))) ||
!parentOwner) {
return false;
}
nsCOMPtr<nsIXULWindow> window(do_GetInterface(parentOwner));
if (window && NS_FAILED(window->GetChromeFlags(&chromeFlags))) {
return false;
}
PROFILER_LABEL("nsFrameLoader", "CreateRemoteBrowser",
js::ProfileEntry::Category::OTHER);
MutableTabContext context;
nsresult rv = GetNewTabContext(&context);
NS_ENSURE_SUCCESS(rv, false);
nsCOMPtr<Element> ownerElement = mOwnerContent;
mRemoteBrowser = ContentParent::CreateBrowserOrApp(context, ownerElement, openerContentParent);
if (!mRemoteBrowser) {
return false;
}
MaybeUpdatePrimaryTabParent(eTabParentChanged);
mChildID = mRemoteBrowser->Manager()->ChildID();
nsCOMPtr<nsIDocShellTreeItem> rootItem;
parentDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
nsCOMPtr<nsIDOMWindow> rootWin = rootItem->GetWindow();
nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(rootWin);
if (rootChromeWin) {
nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
mRemoteBrowser->SetBrowserDOMWindow(browserDOMWin);
}
if (mOwnerContent->AttrValueIs(kNameSpaceID_None,
nsGkAtoms::mozpasspointerevents,
nsGkAtoms::_true,
eCaseMatters)) {
Unused << mRemoteBrowser->SendSetUpdateHitRegion(true);
}
ReallyLoadFrameScripts();
InitializeBrowserAPI();
return true;
}
mozilla::dom::PBrowserParent*
nsFrameLoader::GetRemoteBrowser() const
{
return mRemoteBrowser;
}
RenderFrameParent*
nsFrameLoader::GetCurrentRenderFrame() const
{
if (mRemoteBrowser) {
return mRemoteBrowser->GetRenderFrame();
}
return nullptr;
}
NS_IMETHODIMP
nsFrameLoader::ActivateRemoteFrame() {
if (mRemoteBrowser) {
mRemoteBrowser->Activate();
return NS_OK;
}
return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
nsFrameLoader::DeactivateRemoteFrame() {
if (mRemoteBrowser) {
mRemoteBrowser->Deactivate();
return NS_OK;
}
return NS_ERROR_UNEXPECTED;
}
void
nsFrameLoader::ActivateUpdateHitRegion() {
if (mRemoteBrowser) {
Unused << mRemoteBrowser->SendSetUpdateHitRegion(true);
}
}
void
nsFrameLoader::DeactivateUpdateHitRegion() {
if (mRemoteBrowser) {
Unused << mRemoteBrowser->SendSetUpdateHitRegion(false);
}
}
NS_IMETHODIMP
nsFrameLoader::SendCrossProcessMouseEvent(const nsAString& aType,
float aX,
float aY,
int32_t aButton,
int32_t aClickCount,
int32_t aModifiers,
bool aIgnoreRootScrollFrame)
{
if (mRemoteBrowser) {
mRemoteBrowser->SendMouseEvent(aType, aX, aY, aButton,
aClickCount, aModifiers,
aIgnoreRootScrollFrame);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsFrameLoader::ActivateFrameEvent(const nsAString& aType,
bool aCapture)
{
if (mRemoteBrowser) {
return mRemoteBrowser->SendActivateFrameEvent(nsString(aType), aCapture) ?
NS_OK : NS_ERROR_NOT_AVAILABLE;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsFrameLoader::SendCrossProcessKeyEvent(const nsAString& aType,
int32_t aKeyCode,
int32_t aCharCode,
int32_t aModifiers,
bool aPreventDefault)
{
if (mRemoteBrowser) {
mRemoteBrowser->SendKeyEvent(aType, aKeyCode, aCharCode, aModifiers,
aPreventDefault);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
nsresult
nsFrameLoader::CreateStaticClone(nsIFrameLoader* aDest)
{
nsFrameLoader* dest = static_cast<nsFrameLoader*>(aDest);
dest->MaybeCreateDocShell();
NS_ENSURE_STATE(dest->mDocShell);
nsCOMPtr<nsIDocument> dummy = dest->mDocShell->GetDocument();
nsCOMPtr<nsIContentViewer> viewer;
dest->mDocShell->GetContentViewer(getter_AddRefs(viewer));
NS_ENSURE_STATE(viewer);
nsCOMPtr<nsIDocShell> origDocShell;
GetDocShell(getter_AddRefs(origDocShell));
NS_ENSURE_STATE(origDocShell);
nsCOMPtr<nsIDocument> doc = origDocShell->GetDocument();
NS_ENSURE_STATE(doc);
nsCOMPtr<nsIDocument> clonedDoc = doc->CreateStaticClone(dest->mDocShell);
nsCOMPtr<nsIDOMDocument> clonedDOMDoc = do_QueryInterface(clonedDoc);
viewer->SetDOMDocument(clonedDOMDoc);
return NS_OK;
}
bool
nsFrameLoader::DoLoadMessageManagerScript(const nsAString& aURL, bool aRunInGlobalScope)
{
auto* tabParent = TabParent::GetFrom(GetRemoteBrowser());
if (tabParent) {
return tabParent->SendLoadRemoteScript(nsString(aURL), aRunInGlobalScope);
}
RefPtr<nsInProcessTabChildGlobal> tabChild =
static_cast<nsInProcessTabChildGlobal*>(GetTabChildGlobalAsEventTarget());
if (tabChild) {
tabChild->LoadFrameScript(aURL, aRunInGlobalScope);
}
return true;
}
class nsAsyncMessageToChild : public nsSameProcessAsyncMessageBase,
public nsRunnable
{
public:
nsAsyncMessageToChild(JSContext* aCx, JS::Handle<JSObject*> aCpows, nsFrameLoader* aFrameLoader)
: nsSameProcessAsyncMessageBase(aCx, aCpows)
, mFrameLoader(aFrameLoader)
{
}
NS_IMETHOD Run()
{
nsInProcessTabChildGlobal* tabChild =
static_cast<nsInProcessTabChildGlobal*>(mFrameLoader->mChildMessageManager.get());
// Since bug 1126089, messages can arrive even when the docShell is destroyed.
// Here we make sure that those messages are not delivered.
if (tabChild && tabChild->GetInnerManager() && mFrameLoader->GetExistingDocShell()) {
nsCOMPtr<nsIXPConnectJSObjectHolder> kungFuDeathGrip(tabChild->GetGlobal());
ReceiveMessage(static_cast<EventTarget*>(tabChild), mFrameLoader,
tabChild->GetInnerManager());
}
return NS_OK;
}
RefPtr<nsFrameLoader> mFrameLoader;
};
nsresult
nsFrameLoader::DoSendAsyncMessage(JSContext* aCx,
const nsAString& aMessage,
StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
nsIPrincipal* aPrincipal)
{
TabParent* tabParent = mRemoteBrowser;
if (tabParent) {
ClonedMessageData data;
nsIContentParent* cp = tabParent->Manager();
if (!BuildClonedMessageDataForParent(cp, aData, data)) {
MOZ_CRASH();
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
InfallibleTArray<mozilla::jsipc::CpowEntry> cpows;
jsipc::CPOWManager* mgr = cp->GetCPOWManager();
if (aCpows && (!mgr || !mgr->Wrap(aCx, aCpows, &cpows))) {
return NS_ERROR_UNEXPECTED;
}
if (tabParent->SendAsyncMessage(nsString(aMessage), data, cpows,
IPC::Principal(aPrincipal))) {
return NS_OK;
} else {
return NS_ERROR_UNEXPECTED;
}
}
if (mChildMessageManager) {
RefPtr<nsAsyncMessageToChild> ev = new nsAsyncMessageToChild(aCx, aCpows, this);
nsresult rv = ev->Init(aCx, aMessage, aData, aPrincipal);
if (NS_FAILED(rv)) {
return rv;
}
rv = NS_DispatchToCurrentThread(ev);
if (NS_FAILED(rv)) {
return rv;
}
return rv;
}
// We don't have any targets to send our asynchronous message to.
return NS_ERROR_UNEXPECTED;
}
bool
nsFrameLoader::CheckPermission(const nsAString& aPermission)
{
return AssertAppProcessPermission(GetRemoteBrowser(),
NS_ConvertUTF16toUTF8(aPermission).get());
}
bool
nsFrameLoader::CheckManifestURL(const nsAString& aManifestURL)
{
return AssertAppProcessManifestURL(GetRemoteBrowser(),
NS_ConvertUTF16toUTF8(aManifestURL).get());
}
bool
nsFrameLoader::CheckAppHasPermission(const nsAString& aPermission)
{
return AssertAppHasPermission(GetRemoteBrowser(),
NS_ConvertUTF16toUTF8(aPermission).get());
}
NS_IMETHODIMP
nsFrameLoader::GetMessageManager(nsIMessageSender** aManager)
{
EnsureMessageManager();
if (mMessageManager) {
RefPtr<nsFrameMessageManager> mm(mMessageManager);
mm.forget(aManager);
return NS_OK;
}
return NS_OK;
}
nsresult
nsFrameLoader::EnsureMessageManager()
{
NS_ENSURE_STATE(mOwnerContent);
if (mMessageManager) {
return NS_OK;
}
if (!mIsTopLevelContent &&
!OwnerIsBrowserOrAppFrame() &&
!IsRemoteFrame() &&
!(mOwnerContent->IsXULElement() &&
mOwnerContent->AttrValueIs(kNameSpaceID_None,
nsGkAtoms::forcemessagemanager,
nsGkAtoms::_true, eCaseMatters))) {
return NS_OK;
}
nsCOMPtr<nsIDOMChromeWindow> chromeWindow =
do_QueryInterface(GetOwnerDoc()->GetWindow());
nsCOMPtr<nsIMessageBroadcaster> parentManager;
if (chromeWindow) {
nsAutoString messagemanagergroup;
if (mOwnerContent->IsXULElement() &&
mOwnerContent->GetAttr(kNameSpaceID_None,
nsGkAtoms::messagemanagergroup,
messagemanagergroup)) {
chromeWindow->GetGroupMessageManager(messagemanagergroup, getter_AddRefs(parentManager));
}
if (!parentManager) {
chromeWindow->GetMessageManager(getter_AddRefs(parentManager));
}
}
mMessageManager = new nsFrameMessageManager(nullptr,
static_cast<nsFrameMessageManager*>(parentManager.get()),
MM_CHROME);
if (!IsRemoteFrame()) {
nsresult rv = MaybeCreateDocShell();
if (NS_FAILED(rv)) {
return rv;
}
NS_ASSERTION(mDocShell,
"MaybeCreateDocShell succeeded, but null mDocShell");
if (!mDocShell) {
return NS_ERROR_FAILURE;
}
mChildMessageManager =
new nsInProcessTabChildGlobal(mDocShell, mOwnerContent, mMessageManager);
}
return NS_OK;
}
nsresult
nsFrameLoader::ReallyLoadFrameScripts()
{
nsresult rv = EnsureMessageManager();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (mMessageManager) {
mMessageManager->InitWithCallback(this);
}
return NS_OK;
}
EventTarget*
nsFrameLoader::GetTabChildGlobalAsEventTarget()
{
return static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get());
}
NS_IMETHODIMP
nsFrameLoader::GetOwnerElement(nsIDOMElement **aElement)
{
nsCOMPtr<nsIDOMElement> ownerElement = do_QueryInterface(mOwnerContent);
ownerElement.forget(aElement);
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::GetChildID(uint64_t* aChildID)
{
*aChildID = mChildID;
return NS_OK;
}
void
nsFrameLoader::SetRemoteBrowser(nsITabParent* aTabParent)
{
MOZ_ASSERT(!mRemoteBrowser);
mRemoteFrame = true;
mRemoteBrowser = TabParent::GetFrom(aTabParent);
mChildID = mRemoteBrowser ? mRemoteBrowser->Manager()->ChildID() : 0;
MaybeUpdatePrimaryTabParent(eTabParentChanged);
ReallyLoadFrameScripts();
InitializeBrowserAPI();
ShowRemoteFrame(ScreenIntSize(0, 0));
}
nsresult
nsFrameLoader::SwapRemoteBrowser(nsITabParent* aTabParent)
{
RefPtr<TabParent> newParent = TabParent::GetFrom(aTabParent);
if (!newParent || !mRemoteBrowser) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
if (!IsRemoteFrame()) {
NS_WARNING("Switching from in-process to out-of-process is not supported.");
return NS_ERROR_NOT_IMPLEMENTED;
}
if (!OwnerIsBrowserOrAppFrame()) {
NS_WARNING("Switching process for non-mozbrowser/app frame is not supported.");
return NS_ERROR_NOT_IMPLEMENTED;
}
if (newParent == mRemoteBrowser) {
return NS_OK;
}
MaybeUpdatePrimaryTabParent(eTabParentRemoved);
mRemoteBrowser->CacheFrameLoader(nullptr);
mRemoteBrowser->SetOwnerElement(nullptr);
mRemoteBrowser->Detach();
mRemoteBrowser->Destroy();
mRemoteBrowser = newParent;
mRemoteBrowser->Attach(this);
mChildID = mRemoteBrowser->Manager()->ChildID();
MaybeUpdatePrimaryTabParent(eTabParentChanged);
// Force the new remote frame manager to load pending scripts
mMessageManager->LoadPendingScripts();
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
"remote-browser-swapped", nullptr);
}
if (!mRemoteBrowserShown) {
ShowRemoteFrame(ScreenIntSize(0, 0));
}
return NS_OK;
}
void
nsFrameLoader::SetDetachedSubdocFrame(nsIFrame* aDetachedFrame,
nsIDocument* aContainerDoc)
{
mDetachedSubdocFrame = aDetachedFrame;
mContainerDocWhileDetached = aContainerDoc;
}
nsIFrame*
nsFrameLoader::GetDetachedSubdocFrame(nsIDocument** aContainerDoc) const
{
NS_IF_ADDREF(*aContainerDoc = mContainerDocWhileDetached);
return mDetachedSubdocFrame.GetFrame();
}
void
nsFrameLoader::ApplySandboxFlags(uint32_t sandboxFlags)
{
if (mDocShell) {
uint32_t parentSandboxFlags = mOwnerContent->OwnerDoc()->GetSandboxFlags();
// The child can only add restrictions, never remove them.
sandboxFlags |= parentSandboxFlags;
mDocShell->SetSandboxFlags(sandboxFlags);
}
}
/* virtual */ void
nsFrameLoader::AttributeChanged(nsIDocument* aDocument,
mozilla::dom::Element* aElement,
int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType,
const nsAttrValue* aOldValue)
{
MOZ_ASSERT(mObservingOwnerContent);
if (aNameSpaceID != kNameSpaceID_None || aAttribute != TypeAttrName()) {
return;
}
if (aElement != mOwnerContent) {
return;
}
// Note: This logic duplicates a lot of logic in
// MaybeCreateDocshell. We should fix that.
// Notify our enclosing chrome that our type has changed. We only do this
// if our parent is chrome, since in all other cases we're random content
// subframes and the treeowner shouldn't worry about us.
if (!mDocShell) {
MaybeUpdatePrimaryTabParent(eTabParentChanged);
return;
}
nsCOMPtr<nsIDocShellTreeItem> parentItem;
mDocShell->GetParent(getter_AddRefs(parentItem));
if (!parentItem) {
return;
}
if (parentItem->ItemType() != nsIDocShellTreeItem::typeChrome) {
return;
}
nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
parentItem->GetTreeOwner(getter_AddRefs(parentTreeOwner));
if (!parentTreeOwner) {
return;
}
nsAutoString value;
aElement->GetAttr(kNameSpaceID_None, TypeAttrName(), value);
bool is_primary = value.LowerCaseEqualsLiteral("content-primary");
#ifdef MOZ_XUL
// when a content panel is no longer primary, hide any open popups it may have
if (!is_primary) {
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (pm)
pm->HidePopupsInDocShell(mDocShell);
}
#endif
parentTreeOwner->ContentShellRemoved(mDocShell);
if (value.LowerCaseEqualsLiteral("content") ||
StringBeginsWith(value, NS_LITERAL_STRING("content-"),
nsCaseInsensitiveStringComparator())) {
bool is_targetable = is_primary ||
value.LowerCaseEqualsLiteral("content-targetable");
parentTreeOwner->ContentShellAdded(mDocShell, is_primary,
is_targetable, value);
}
}
void
nsFrameLoader::ResetPermissionManagerStatus()
{
// The resetting of the permissions status can run only
// in the main process.
// only in-main-process && in-process frame is handled here and all other
// cases are handled by ContentParent.
if (XRE_IsContentProcess() || mRemoteFrame) {
return;
}
// Finding the new app Id:
// . first we check if the owner is an app frame
// . second, we check if the owner is a browser frame
// in both cases we populate the appId variable.
uint32_t appId = nsIScriptSecurityManager::NO_APP_ID;
if (OwnerIsAppFrame()) {
// You can't be both an app and a browser frame.
MOZ_ASSERT(!OwnerIsBrowserFrame());
nsCOMPtr<mozIApplication> ownApp = GetOwnApp();
MOZ_ASSERT(ownApp);
uint32_t ownAppId = nsIScriptSecurityManager::NO_APP_ID;
if (ownApp && NS_SUCCEEDED(ownApp->GetLocalId(&ownAppId))) {
appId = ownAppId;
}
}
if (OwnerIsBrowserFrame()) {
// You can't be both a browser and an app frame.
MOZ_ASSERT(!OwnerIsAppFrame());
nsCOMPtr<mozIApplication> containingApp = GetContainingApp();
uint32_t containingAppId = nsIScriptSecurityManager::NO_APP_ID;
if (containingApp && NS_SUCCEEDED(containingApp->GetLocalId(&containingAppId))) {
appId = containingAppId;
}
}
// Nothing changed.
if (appId == mAppIdSentToPermissionManager) {
return;
}
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
if (!permMgr) {
NS_ERROR("No PermissionManager available!");
return;
}
// If previously we registered an appId, we have to unregister it.
if (mAppIdSentToPermissionManager != nsIScriptSecurityManager::NO_APP_ID) {
permMgr->ReleaseAppId(mAppIdSentToPermissionManager);
mAppIdSentToPermissionManager = nsIScriptSecurityManager::NO_APP_ID;
}
// Register the new AppId.
if (appId != nsIScriptSecurityManager::NO_APP_ID) {
mAppIdSentToPermissionManager = appId;
permMgr->AddrefAppId(mAppIdSentToPermissionManager);
}
}
/**
* Send the RequestNotifyAfterRemotePaint message to the current Tab.
*/
NS_IMETHODIMP
nsFrameLoader::RequestNotifyAfterRemotePaint()
{
// If remote browsing (e10s), handle this with the TabParent.
if (mRemoteBrowser) {
Unused << mRemoteBrowser->SendRequestNotifyAfterRemotePaint();
}
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::RequestNotifyLayerTreeReady()
{
if (mRemoteBrowser) {
return mRemoteBrowser->RequestNotifyLayerTreeReady() ? NS_OK : NS_ERROR_NOT_AVAILABLE;
}
if (!mOwnerContent) {
return NS_ERROR_NOT_AVAILABLE;
}
RefPtr<AsyncEventDispatcher> event =
new AsyncEventDispatcher(mOwnerContent,
NS_LITERAL_STRING("MozLayerTreeReady"),
true, false);
event->PostDOMEvent();
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::RequestNotifyLayerTreeCleared()
{
if (mRemoteBrowser) {
return mRemoteBrowser->RequestNotifyLayerTreeCleared() ? NS_OK : NS_ERROR_NOT_AVAILABLE;
}
if (!mOwnerContent) {
return NS_ERROR_NOT_AVAILABLE;
}
RefPtr<AsyncEventDispatcher> event =
new AsyncEventDispatcher(mOwnerContent,
NS_LITERAL_STRING("MozLayerTreeCleared"),
true, false);
event->PostDOMEvent();
return NS_OK;
}
/* [infallible] */ NS_IMETHODIMP
nsFrameLoader::SetVisible(bool aVisible)
{
if (mVisible == aVisible) {
return NS_OK;
}
mVisible = aVisible;
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
"frameloader-visible-changed", nullptr);
}
return NS_OK;
}
/* [infallible] */ NS_IMETHODIMP
nsFrameLoader::GetVisible(bool* aVisible)
{
*aVisible = mVisible;
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::GetTabParent(nsITabParent** aTabParent)
{
nsCOMPtr<nsITabParent> tp = mRemoteBrowser;
tp.forget(aTabParent);
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::GetLoadContext(nsILoadContext** aLoadContext)
{
nsCOMPtr<nsILoadContext> loadContext;
if (mRemoteBrowser) {
loadContext = mRemoteBrowser->GetLoadContext();
} else {
nsCOMPtr<nsIDocShell> docShell;
GetDocShell(getter_AddRefs(docShell));
loadContext = do_GetInterface(docShell);
}
loadContext.forget(aLoadContext);
return NS_OK;
}
void
nsFrameLoader::InitializeBrowserAPI()
{
if (OwnerIsBrowserOrAppFrame()) {
if (!IsRemoteFrame()) {
nsresult rv = EnsureMessageManager();
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
if (mMessageManager) {
mMessageManager->LoadFrameScript(
NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"),
/* allowDelayedLoad = */ true,
/* aRunInGlobalScope */ true);
}
}
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
if (browserFrame) {
browserFrame->InitializeBrowserAPI();
}
}
}
void
nsFrameLoader::MaybeUpdatePrimaryTabParent(TabParentChange aChange)
{
if (mRemoteBrowser && mOwnerContent) {
nsCOMPtr<nsIDocShell> docShell = mOwnerContent->OwnerDoc()->GetDocShell();
if (!docShell) {
return;
}
int32_t parentType = docShell->ItemType();
if (parentType != nsIDocShellTreeItem::typeChrome) {
return;
}
nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
docShell->GetTreeOwner(getter_AddRefs(parentTreeOwner));
if (!parentTreeOwner) {
return;
}
if (!mObservingOwnerContent) {
mOwnerContent->AddMutationObserver(this);
mObservingOwnerContent = true;
}
parentTreeOwner->TabParentRemoved(mRemoteBrowser);
if (aChange == eTabParentChanged) {
bool isPrimary =
mOwnerContent->AttrValueIs(kNameSpaceID_None,
TypeAttrName(),
NS_LITERAL_STRING("content-primary"),
eIgnoreCase);
parentTreeOwner->TabParentAdded(mRemoteBrowser, isPrimary);
}
}
}
nsresult
nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext,
const nsACString& aSignedPkgOriginNoSuffix,
const nsACString& aPackageId)
{
nsCOMPtr<mozIApplication> ownApp = GetOwnApp();
nsCOMPtr<mozIApplication> containingApp = GetContainingApp();
OriginAttributes attrs = OriginAttributes();
attrs.mInBrowser = OwnerIsBrowserFrame();
// Get the AppId from ownApp
uint32_t appId = nsIScriptSecurityManager::NO_APP_ID;
if (ownApp) {
nsresult rv = ownApp->GetLocalId(&appId);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_STATE(appId != nsIScriptSecurityManager::NO_APP_ID);
} else if (containingApp) {
nsresult rv = containingApp->GetLocalId(&appId);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_STATE(appId != nsIScriptSecurityManager::NO_APP_ID);
}
attrs.mAppId = appId;
// Populate packageId to signedPkg.
attrs.mSignedPkg = NS_ConvertUTF8toUTF16(aPackageId);
// set the userContextId on the attrs before we pass them into
// the tab context
if (mOwnerContent) {
nsAutoString userContextIdStr;
if (mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usercontextid)) {
mOwnerContent->GetAttr(kNameSpaceID_None,
nsGkAtoms::usercontextid,
userContextIdStr);
}
if (!userContextIdStr.IsEmpty()) {
nsresult err;
uint32_t userContextId = userContextIdStr.ToInteger(&err);
NS_ENSURE_SUCCESS(err, err);
attrs.mUserContextId = userContextId;
}
}
bool tabContextUpdated = aTabContext->SetTabContext(ownApp, containingApp,
attrs, aSignedPkgOriginNoSuffix);
NS_ENSURE_STATE(tabContextUpdated);
return NS_OK;
}