Bug 1595491 - Part 1: Make <embed> and <object> behave more like <iframe>. r=smaug,emilio

By making image loading in <embed> and <object> behave more like when
an <iframe> loads an image, we can make sure that the synthetic
document generated is process switched if the image is cross
origin. This is done by making image loading in nsObjectLoadingContent
follow the document loading path.

We also make sure that we pass the image size back to the embedder
element to not get stuck with the intrinsic size.

To avoid named targeting being able to target these synthetic
documents, as well as showing up in `Window.frames` and being counted
in `Window.length`, we keep a filtered list of non-synthetic browsing
contexts for that use-case.

This feature is controlled by two prefs:

* browser.opaqueResponseBlocking.syntheticBrowsingContext

  This triggers the creation of synthetic documents for images loaded
  in <object> or embed.

* browser.opaqueResponseBlocking.syntheticBrowsingContext.filter

  This turns on the filtering of synthetic browsing contexts in named
  targeting, `Window.length` and `Window.frames`.

Differential Revision: https://phabricator.services.mozilla.com/D148117
This commit is contained in:
Andreas Farre 2022-08-12 15:48:26 +00:00
Родитель 90178fe177
Коммит ec3d91462e
39 изменённых файлов: 755 добавлений и 66 удалений

Просмотреть файл

@ -737,9 +737,22 @@ void BrowsingContext::SetEmbedderElement(Element* aEmbedder) {
obs->NotifyWhenScriptSafe(ToSupports(this),
"browsing-context-did-set-embedder", nullptr);
}
if (IsEmbedderTypeObjectOrEmbed()) {
if (WindowContext* context = GetParentWindowContext()) {
Unused << SetSyntheticDocumentContainer(true);
}
}
}
}
bool BrowsingContext::IsEmbedderTypeObjectOrEmbed() {
if (const Maybe<nsString>& type = GetEmbedderElementType()) {
return nsGkAtoms::object->Equals(*type) || nsGkAtoms::embed->Equals(*type);
}
return false;
}
void BrowsingContext::Embed() {
if (auto* frame = HTMLIFrameElement::FromNode(mEmbedderElement)) {
frame->BindToBrowsingContext(this);
@ -1059,6 +1072,13 @@ void BrowsingContext::GetChildren(
aChildren.AppendElements(Children());
}
Span<RefPtr<BrowsingContext>> BrowsingContext::NonSyntheticChildren() const {
if (WindowContext* current = mCurrentWindowContext) {
return current->NonSyntheticChildren();
}
return Span<RefPtr<BrowsingContext>>();
}
void BrowsingContext::GetWindowContexts(
nsTArray<RefPtr<WindowContext>>& aWindows) {
aWindows.AppendElements(mWindowContexts);
@ -1209,7 +1229,7 @@ BrowsingContext* BrowsingContext::FindWithName(
found = parent;
break;
} else {
siblings = parent->Children();
siblings = parent->NonSyntheticChildren();
}
for (BrowsingContext* sibling : siblings) {
@ -1244,7 +1264,7 @@ BrowsingContext* BrowsingContext::FindChildWithName(
return nullptr;
}
for (BrowsingContext* child : Children()) {
for (BrowsingContext* child : NonSyntheticChildren()) {
if (child->NameEquals(aName) && aRequestingContext.CanAccess(child) &&
child->IsTargetable()) {
return child;
@ -1288,7 +1308,7 @@ BrowsingContext* BrowsingContext::FindWithNameInSubtree(
return this;
}
for (BrowsingContext* child : Children()) {
for (BrowsingContext* child : NonSyntheticChildren()) {
if (BrowsingContext* found =
child->FindWithNameInSubtree(aName, aRequestingContext)) {
return found;
@ -2966,6 +2986,13 @@ void BrowsingContext::DidSet(FieldIndex<IDX_IsInBFCache>) {
}
}
void BrowsingContext::DidSet(FieldIndex<IDX_SyntheticDocumentContainer>) {
if (WindowContext* parentWindowContext = GetParentWindowContext()) {
parentWindowContext->UpdateChildSynthetic(this,
GetSyntheticDocumentContainer());
}
}
void BrowsingContext::SetCustomPlatform(const nsAString& aPlatform,
ErrorResult& aRv) {
Top()->SetPlatformOverride(aPlatform, aRv);

Просмотреть файл

@ -234,7 +234,10 @@ enum class ExplicitActiveStatus : uint8_t {
/* This field only gets incrememented when we start navigations in the \
* parent process. This is used for keeping track of the racing navigations \
* between the parent and content processes. */ \
FIELD(ParentInitiatedNavigationEpoch, uint64_t)
FIELD(ParentInitiatedNavigationEpoch, uint64_t) \
/* This browsing context is for a synthetic image document wrapping an \
* image embedded in <object> or <embed>. */ \
FIELD(SyntheticDocumentContainer, bool)
// BrowsingContext, in this context, is the cross process replicated
// environment in which information about documents is stored. In
@ -366,6 +369,10 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
Element* GetEmbedderElement() const { return mEmbedderElement; }
void SetEmbedderElement(Element* aEmbedder);
// Return true if the type of the embedder element is either object
// or embed, false otherwise.
bool IsEmbedderTypeObjectOrEmbed();
// Called after the BrowingContext has been embedded in a FrameLoader. This
// happens after `SetEmbedderElement` is called on the BrowsingContext and
// after the BrowsingContext has been set on the FrameLoader.
@ -488,6 +495,8 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
Span<RefPtr<BrowsingContext>> Children() const;
void GetChildren(nsTArray<RefPtr<BrowsingContext>>& aChildren);
Span<RefPtr<BrowsingContext>> NonSyntheticChildren() const;
const nsTArray<RefPtr<WindowContext>>& GetWindowContexts() {
return mWindowContexts;
}
@ -1194,6 +1203,8 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
bool CanSet(FieldIndex<IDX_IsInBFCache>, bool, ContentParent* aSource);
void DidSet(FieldIndex<IDX_IsInBFCache>);
void DidSet(FieldIndex<IDX_SyntheticDocumentContainer>);
// Allow if the process attemping to set field is the same as the owning
// process. Deprecated. New code that might use this should generally be moved
// to WindowContext or be settable only by the parent process.

Просмотреть файл

@ -122,6 +122,9 @@ void WindowContext::AppendChildBrowsingContext(
MOZ_DIAGNOSTIC_ASSERT(!mChildren.Contains(aBrowsingContext));
mChildren.AppendElement(aBrowsingContext);
if (!aBrowsingContext->IsEmbedderTypeObjectOrEmbed()) {
mNonSyntheticChildren.AppendElement(aBrowsingContext);
}
// If we're the current WindowContext in our BrowsingContext, make sure to
// clear any cached `children` value.
@ -136,6 +139,7 @@ void WindowContext::RemoveChildBrowsingContext(
"Mismatched groups?");
mChildren.RemoveElement(aBrowsingContext);
mNonSyntheticChildren.RemoveElement(aBrowsingContext);
// If we're the current WindowContext in our BrowsingContext, make sure to
// clear any cached `children` value.
@ -144,6 +148,19 @@ void WindowContext::RemoveChildBrowsingContext(
}
}
void WindowContext::UpdateChildSynthetic(BrowsingContext* aBrowsingContext,
bool aIsSynthetic) {
if (aIsSynthetic) {
mNonSyntheticChildren.RemoveElement(aBrowsingContext);
} else {
// The same BrowsingContext will be reused for error pages, so it can be in
// the list already.
if (!mNonSyntheticChildren.Contains(aBrowsingContext)) {
mNonSyntheticChildren.AppendElement(aBrowsingContext);
}
}
}
void WindowContext::SendCommitTransaction(ContentParent* aParent,
const BaseTransaction& aTxn,
uint64_t aEpoch) {
@ -569,12 +586,14 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WindowContext)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildren)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mNonSyntheticChildren)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WindowContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildren)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNonSyntheticChildren)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(WindowContext)

Просмотреть файл

@ -147,6 +147,12 @@ class WindowContext : public nsISupports, public nsWrapperCache {
Span<RefPtr<BrowsingContext>> Children() { return mChildren; }
// The filtered version of `Children()`, which contains no browsing contexts
// for synthetic documents as created by object loading content.
Span<RefPtr<BrowsingContext>> NonSyntheticChildren() {
return mNonSyntheticChildren;
}
// Cast this object to it's parent-process canonical form.
WindowGlobalParent* Canonical();
@ -219,6 +225,12 @@ class WindowContext : public nsISupports, public nsWrapperCache {
void AppendChildBrowsingContext(BrowsingContext* aBrowsingContext);
void RemoveChildBrowsingContext(BrowsingContext* aBrowsingContext);
// Update non-synthetic children based on whether `aBrowsingContext`
// is synthetic or not. Regardless the synthetic of `aBrowsingContext`, it is
// kept in this WindowContext's all children list.
void UpdateChildSynthetic(BrowsingContext* aBrowsingContext,
bool aIsSynthetic);
// Send a given `BaseTransaction` object to the correct remote.
void SendCommitTransaction(ContentParent* aParent,
const BaseTransaction& aTxn, uint64_t aEpoch);
@ -331,6 +343,15 @@ class WindowContext : public nsISupports, public nsWrapperCache {
// `AppendChildBrowsingContext` and `RemoveChildBrowsingContext` methods.
nsTArray<RefPtr<BrowsingContext>> mChildren;
// --- NEVER CHANGE `mNonSyntheticChildren` DIRECTLY! ---
// Same reason as for mChildren.
// mNonSyntheticChildren contains the same browsing contexts except browsing
// contexts created by the synthetic document for object loading contents
// loading images. This is used to discern browsing contexts created when
// loading images in <object> or <embed> elements, so that they can be hidden
// from named targeting, `Window.frames` etc.
nsTArray<RefPtr<BrowsingContext>> mNonSyntheticChildren;
bool mIsDiscarded = false;
bool mIsInProcess = false;

Просмотреть файл

@ -16519,6 +16519,16 @@ Selection* Document::GetSelection(ErrorResult& aRv) {
return nsGlobalWindowInner::Cast(window)->GetSelection(aRv);
}
void Document::MakeBrowsingContextNonSynthetic() {
if (nsContentUtils::ShouldHideObjectOrEmbedImageDocument()) {
if (BrowsingContext* bc = GetBrowsingContext()) {
if (bc->GetSyntheticDocumentContainer()) {
Unused << bc->SetSyntheticDocumentContainer(false);
}
}
}
}
nsresult Document::HasStorageAccessSync(bool& aHasStorageAccess) {
// Step 1: check if cookie permissions are available or denied to this
// document's principal

Просмотреть файл

@ -270,6 +270,7 @@ class SVGDocument;
class SVGElement;
class SVGSVGElement;
class SVGUseElement;
class ImageDocument;
class Touch;
class TouchList;
class TreeWalker;
@ -1255,6 +1256,7 @@ class Document : public nsINode,
Selection* GetSelection(ErrorResult& aRv);
void MakeBrowsingContextNonSynthetic();
nsresult HasStorageAccessSync(bool& aHasStorageAccess);
already_AddRefed<Promise> HasStorageAccess(ErrorResult& aRv);
@ -2200,6 +2202,9 @@ class Document : public nsINode,
*/
bool IsHTMLDocument() const { return mType == eHTML; }
bool IsHTMLOrXHTML() const { return mType == eHTML || mType == eXHTML; }
bool IsImageDocument() const {
return MediaDocumentKind() == MediaDocumentKind::Image;
}
bool IsXMLDocument() const { return !IsHTMLDocument(); }
bool IsSVGDocument() const { return mType == eSVG; }
bool IsUnstyledDocument() { return IsLoadedAsData(); }
@ -3575,6 +3580,13 @@ class Document : public nsINode,
inline SVGDocument* AsSVGDocument();
inline const SVGDocument* AsSVGDocument() const;
/**
* Asserts IsImageDocument, and can't return null.
* Defined inline in ImageDocument.h
*/
inline ImageDocument* AsImageDocument();
inline const ImageDocument* AsImageDocument() const;
/*
* Given a node, get a weak reference to it and append that reference to
* mBlockedNodesByClassifier. Can be used later on to look up a node in it.

Просмотреть файл

@ -109,6 +109,7 @@
#include "mozilla/ScrollbarPreferences.h"
#include "mozilla/Span.h"
#include "mozilla/StaticAnalysisFunctions.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_dom.h"
#ifdef FUZZING
# include "mozilla/StaticPrefs_fuzzing.h"
@ -10251,7 +10252,7 @@ uint32_t nsContentUtils::HtmlObjectContentTypeForMIMEType(
}
if (imgLoader::SupportImageWithMimeType(aMIMEType)) {
return nsIObjectLoadingContent::TYPE_IMAGE;
return ResolveObjectType(nsIObjectLoadingContent::TYPE_IMAGE);
}
// Faking support of the PDF content as a document for EMBED tags
@ -10970,6 +10971,28 @@ nsresult nsContentUtils::AnonymizeId(nsAString& aId,
return NS_OK;
}
/* static */
bool nsContentUtils::ShouldHideObjectOrEmbedImageDocument() {
return StaticPrefs::
browser_opaqueResponseBlocking_syntheticBrowsingContext_AtStartup() &&
StaticPrefs::
browser_opaqueResponseBlocking_syntheticBrowsingContext_filter_AtStartup_DoNotUseDirectly();
}
/* static */
uint32_t nsContentUtils::ResolveObjectType(uint32_t aType) {
if (!StaticPrefs::
browser_opaqueResponseBlocking_syntheticBrowsingContext_AtStartup()) {
return aType;
}
if (aType != nsIObjectLoadingContent::TYPE_IMAGE) {
return aType;
}
return nsIObjectLoadingContent::TYPE_DOCUMENT;
}
namespace mozilla {
std::ostream& operator<<(std::ostream& aOut,
const PreventDefaultResult aPreventDefaultResult) {

Просмотреть файл

@ -3359,6 +3359,19 @@ class nsContentUtils {
static nsresult AnonymizeId(nsAString& aId, const nsACString& aOriginKey,
OriginFormat aFormat = OriginFormat::Base64);
/**
* Return true if we should hide the synthetic browsing context for <object>
* or <embed> images in synthetic documents.
*/
static bool ShouldHideObjectOrEmbedImageDocument();
/**
* Returns the object type that the object loading content will actually use
* to load the resource. Used for ORB and loading images into synthetic
* documents.
*/
static uint32_t ResolveObjectType(uint32_t aType);
private:
static bool InitializeEventTable();

Просмотреть файл

@ -2495,6 +2495,15 @@ void nsFrameLoader::PropagateIsUnderHiddenEmbedderElement(
}
}
void nsFrameLoader::UpdateRemoteStyle(
mozilla::StyleImageRendering aImageRendering) {
MOZ_DIAGNOSTIC_ASSERT(IsRemoteFrame());
if (auto* browserBridgeChild = GetBrowserBridgeChild()) {
browserBridgeChild->SendUpdateRemoteStyle(aImageRendering);
}
}
void nsFrameLoader::UpdateBaseWindowPositionAndSize(
nsSubDocumentFrame* aIFrame) {
nsCOMPtr<nsIBaseWindow> baseWindow = GetDocShell(IgnoreErrors());

Просмотреть файл

@ -147,6 +147,8 @@ class nsFrameLoader final : public nsStubMutationObserver,
void PropagateIsUnderHiddenEmbedderElement(
bool aIsUnderHiddenEmbedderElement);
void UpdateRemoteStyle(mozilla::StyleImageRendering aImageRendering);
// When creating a nsFrameLoaderOwner which is a static clone, a
// `nsFrameLoader` is not immediately attached to it. Instead, it is added to
// the static clone document's `PendingFrameStaticClones` list.

Просмотреть файл

@ -3366,7 +3366,8 @@ Nullable<WindowProxyHolder> nsGlobalWindowOuter::IndexedGetterOuter(
BrowsingContext* bc = GetBrowsingContext();
NS_ENSURE_TRUE(bc, nullptr);
Span<RefPtr<BrowsingContext>> children = bc->Children();
Span<RefPtr<BrowsingContext>> children = bc->NonSyntheticChildren();
if (aIndex < children.Length()) {
return WindowProxyHolder(children[aIndex]);
}
@ -3994,7 +3995,7 @@ double nsGlobalWindowOuter::GetScrollYOuter() { return GetScrollXY(false).y; }
uint32_t nsGlobalWindowOuter::Length() {
BrowsingContext* bc = GetBrowsingContext();
return bc ? bc->Children().Length() : 0;
return bc ? bc->NonSyntheticChildren().Length() : 0;
}
Nullable<WindowProxyHolder> nsGlobalWindowOuter::GetTopOuter() {

Просмотреть файл

@ -391,7 +391,8 @@ nsObjectLoadingContent::nsObjectLoadingContent()
mIsStopping(false),
mIsLoading(false),
mScriptRequested(false),
mRewrittenYoutubeEmbed(false) {}
mRewrittenYoutubeEmbed(false),
mLoadingSyntheticDocument(false) {}
nsObjectLoadingContent::~nsObjectLoadingContent() {
// Should have been unbound from the tree at this point, and
@ -820,11 +821,17 @@ ElementState nsObjectLoadingContent::ObjectState() const {
case eType_Image:
return ImageState();
case eType_FakePlugin:
case eType_Document:
case eType_Document: {
// These are OK. If documents start to load successfully, they display
// something, and are thus not broken in this sense. The same goes for
// plugins.
return ElementState();
ElementState states = ElementState();
if (mLoadingSyntheticDocument) {
states |= ElementState::LOADING;
states |= ImageState();
}
return states;
}
case eType_Fallback:
// This may end up handled as TYPE_NULL or as a "special" type, as
// chosen by the layout.use-plugin-fallback pref.
@ -1299,6 +1306,12 @@ nsObjectLoadingContent::UpdateObjectParameters() {
LOG(("OBJLC [%p]: NewType #4: %u", this, newType));
}
mLoadingSyntheticDocument =
newType == eType_Document &&
StaticPrefs::
browser_opaqueResponseBlocking_syntheticBrowsingContext_AtStartup() &&
imgLoader::SupportImageWithMimeType(newMime);
///
/// Handle existing channels
///
@ -1408,7 +1421,7 @@ nsresult nsObjectLoadingContent::LoadObject(bool aNotify, bool aForceLoad,
ObjectType oldType = mType;
mType = eType_Fallback;
ConfigureFallback();
NotifyStateChanged(oldType, ObjectState(), true);
NotifyStateChanged(oldType, ObjectState(), true, false);
return NS_OK;
}
@ -1695,7 +1708,7 @@ nsresult nsObjectLoadingContent::LoadObject(bool aNotify, bool aForceLoad,
}
// Notify of our final state
NotifyStateChanged(oldType, oldState, aNotify);
NotifyStateChanged(oldType, oldState, aNotify, false);
NS_ENSURE_TRUE(mIsLoading, NS_OK);
//
@ -1725,7 +1738,7 @@ nsresult nsObjectLoadingContent::LoadObject(bool aNotify, bool aForceLoad,
}
}
if (NS_FAILED(rv) && mIsLoading) {
if ((NS_FAILED(rv) && rv != NS_ERROR_PARSED_DATA_CACHED) && mIsLoading) {
// Since we've already notified of our transition, we can just Unload and
// call ConfigureFallback (which will notify again)
oldType = mType;
@ -1734,7 +1747,7 @@ nsresult nsObjectLoadingContent::LoadObject(bool aNotify, bool aForceLoad,
NS_ENSURE_TRUE(mIsLoading, NS_OK);
CloseChannel();
ConfigureFallback();
NotifyStateChanged(oldType, ObjectState(), true);
NotifyStateChanged(oldType, ObjectState(), true, false);
}
return NS_OK;
@ -1964,7 +1977,7 @@ void nsObjectLoadingContent::Unlink(nsObjectLoadingContent* tmp) {
void nsObjectLoadingContent::UnloadObject(bool aResetState) {
// Don't notify in CancelImageRequests until we transition to a new loaded
// state
// state, but not if we've loaded the image in a synthetic browsing context.
CancelImageRequests(false);
if (mFrameLoader) {
mFrameLoader->Destroy();
@ -1997,14 +2010,14 @@ void nsObjectLoadingContent::UnloadObject(bool aResetState) {
void nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType,
ElementState aOldState,
bool aNotify) {
bool aNotify,
bool aForceRestyle) {
LOG(("OBJLC [%p]: NotifyStateChanged: (%u, %" PRIx64 ") -> (%u, %" PRIx64 ")"
" (notify %i)",
this, aOldType, aOldState.GetInternalValue(), mType,
ObjectState().GetInternalValue(), aNotify));
nsCOMPtr<dom::Element> thisEl =
do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
nsCOMPtr<dom::Element> thisEl = AsContent()->AsElement();
MOZ_ASSERT(thisEl, "must be an element");
// XXX(johns): A good bit of the code below replicates UpdateState(true)
@ -2012,7 +2025,7 @@ void nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType,
// Unfortunately, we do some state changes without notifying
// (e.g. in Fallback when canceling image requests), so we have to
// manually notify object state changes.
thisEl->UpdateState(false);
thisEl->UpdateState(aForceRestyle);
if (!aNotify) {
// We're done here
@ -2030,6 +2043,12 @@ void nsObjectLoadingContent::NotifyStateChanged(ObjectType aOldType,
}
RefPtr<PresShell> presShell = doc->GetPresShell();
// If there is no PresShell or it hasn't been initialized there isn't much to
// do.
if (!presShell || !presShell->DidInitialize()) {
return;
}
if (presShell && (aOldType != mType)) {
presShell->PostRecreateFramesFor(thisEl);
}
@ -2046,13 +2065,13 @@ nsObjectLoadingContent::ObjectType nsObjectLoadingContent::GetTypeOfContent(
(eSupportImages | eSupportDocuments | eSupportPlugins));
LOG(
("OBJLC[%p]: calling HtmlObjectContentTypeForMIMEType: aMIMEType: %s - "
("OBJLC [%p]: calling HtmlObjectContentTypeForMIMEType: aMIMEType: %s - "
"thisContent: %p\n",
this, aMIMEType.get(), thisContent.get()));
auto ret =
static_cast<ObjectType>(nsContentUtils::HtmlObjectContentTypeForMIMEType(
aMIMEType, aNoFakePlugin));
LOG(("OBJLC[%p]: called HtmlObjectContentTypeForMIMEType\n", this));
LOG(("OBJLC [%p]: called HtmlObjectContentTypeForMIMEType\n", this));
return ret;
}
@ -2359,14 +2378,29 @@ void nsObjectLoadingContent::SubdocumentIntrinsicSizeOrRatioChanged(
mSubdocumentIntrinsicSize = aIntrinsicSize;
mSubdocumentIntrinsicRatio = aIntrinsicRatio;
nsCOMPtr<nsIContent> thisContent =
do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
if (nsSubDocumentFrame* sdf = do_QueryFrame(thisContent->GetPrimaryFrame())) {
if (nsSubDocumentFrame* sdf = do_QueryFrame(AsContent()->GetPrimaryFrame())) {
sdf->SubdocumentIntrinsicSizeOrRatioChanged();
}
}
void nsObjectLoadingContent::SubdocumentImageLoadComplete(nsresult aResult) {
ElementState oldState = ObjectState();
ObjectType oldType = mType;
mLoadingSyntheticDocument = false;
if (NS_FAILED(aResult)) {
UnloadObject();
mType = eType_Fallback;
ConfigureFallback();
NotifyStateChanged(oldType, oldState, true, false);
return;
}
MOZ_DIAGNOSTIC_ASSERT(mType == eType_Document);
NotifyStateChanged(oldType, oldState, true, true);
}
void nsObjectLoadingContent::MaybeStoreCrossOriginFeaturePolicy() {
MOZ_DIAGNOSTIC_ASSERT(mFrameLoader);

Просмотреть файл

@ -189,6 +189,8 @@ class nsObjectLoadingContent : public nsImageLoadingContent,
const mozilla::Maybe<mozilla::IntrinsicSize>& aIntrinsicSize,
const mozilla::Maybe<mozilla::AspectRatio>& aIntrinsicRatio);
void SubdocumentImageLoadComplete(nsresult aResult);
protected:
/**
* Begins loading the object when called
@ -448,7 +450,8 @@ class nsObjectLoadingContent : public nsImageLoadingContent,
* @param aNotify if false, only need to update the state of our element.
*/
void NotifyStateChanged(ObjectType aOldType,
mozilla::dom::ElementState aOldState, bool aNotify);
mozilla::dom::ElementState aOldState, bool aNotify,
bool aForceRestyle);
/**
* Returns a ObjectType value corresponding to the type of content we would
@ -574,6 +577,8 @@ class nsObjectLoadingContent : public nsImageLoadingContent,
// videos.
bool mRewrittenYoutubeEmbed : 1;
bool mLoadingSyntheticDocument : 1;
nsTArray<mozilla::dom::MozPluginParameter> mCachedAttributes;
nsTArray<mozilla::dom::MozPluginParameter> mCachedParameters;

Просмотреть файл

@ -7,6 +7,7 @@
#include "ImageDocument.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/ComputedStyle.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/ImageDocumentBinding.h"
@ -16,6 +17,8 @@
#include "mozilla/PresShell.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_privacy.h"
#include "nsICSSDeclaration.h"
#include "nsObjectLoadingContent.h"
#include "nsRect.h"
#include "nsIImageLoadingContent.h"
#include "nsGenericHTMLElement.h"
@ -137,6 +140,7 @@ ImageDocument::ImageDocument()
mObservingImageLoader(false),
mTitleUpdateInProgress(false),
mHasCustomTitle(false),
mIsInObjectOrEmbed(false),
mOriginalZoomLevel(1.0),
mOriginalResolution(1.0) {}
@ -175,6 +179,10 @@ nsresult ImageDocument::StartDocumentLoad(
mOriginalZoomLevel = IsSiteSpecific() ? 1.0 : GetZoomLevel();
mOriginalResolution = GetResolution();
if (BrowsingContext* context = GetBrowsingContext()) {
mIsInObjectOrEmbed = context->IsEmbedderTypeObjectOrEmbed();
}
NS_ASSERTION(aDocListener, "null aDocListener");
*aDocListener = new ImageListener(this);
NS_ADDREF(*aDocListener);
@ -344,7 +352,9 @@ void ImageDocument::RestoreImage() {
imageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::width, true);
imageContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::height, true);
if (ImageIsOverflowing()) {
if (mIsInObjectOrEmbed) {
SetModeClass(eIsInObjectOrEmbed);
} else if (ImageIsOverflowing()) {
if (!ImageIsOverflowingVertically()) {
SetModeClass(eOverflowingHorizontalOnly);
} else {
@ -422,6 +432,10 @@ void ImageDocument::SetModeClass(eModeClasses mode) {
} else {
classList->Remove(u"overflowingHorizontalOnly"_ns, IgnoreErrors());
}
if (mode == eIsInObjectOrEmbed) {
classList->Add(u"isInObjectOrEmbed"_ns, IgnoreErrors());
}
}
void ImageDocument::OnSizeAvailable(imgIRequest* aRequest,
@ -462,6 +476,8 @@ void ImageDocument::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus) {
mImageContent->SetAttr(kNameSpaceID_None, nsGkAtoms::alt, errorMsg, false);
}
MaybeSendResultToEmbedder(aStatus);
}
NS_IMETHODIMP
@ -471,7 +487,8 @@ ImageDocument::HandleEvent(Event* aEvent) {
if (eventType.EqualsLiteral("resize")) {
CheckOverflowing(false);
} else if (eventType.EqualsLiteral("click") &&
StaticPrefs::browser_enable_click_image_resizing()) {
StaticPrefs::browser_enable_click_image_resizing() &&
!mIsInObjectOrEmbed) {
ResetZoomLevel();
mShouldResize = true;
if (mImageIsResized) {
@ -526,6 +543,31 @@ void ImageDocument::UpdateSizeFromLayout() {
}
}
void ImageDocument::UpdateRemoteStyle(StyleImageRendering aImageRendering) {
if (!mImageContent) {
return;
}
nsCOMPtr<nsICSSDeclaration> style = mImageContent->Style();
switch (aImageRendering) {
case StyleImageRendering::Auto:
case StyleImageRendering::Smooth:
case StyleImageRendering::Optimizequality:
style->SetProperty("image-rendering"_ns, "auto"_ns, ""_ns,
IgnoreErrors());
break;
case StyleImageRendering::Optimizespeed:
case StyleImageRendering::Pixelated:
style->SetProperty("image-rendering"_ns, "pixelated"_ns, ""_ns,
IgnoreErrors());
break;
case StyleImageRendering::CrispEdges:
style->SetProperty("image-rendering"_ns, "crisp-edges"_ns, ""_ns,
IgnoreErrors());
break;
}
}
nsresult ImageDocument::CreateSyntheticDocument() {
// Synthesize an html document that refers to the image
nsresult rv = MediaDocument::CreateSyntheticDocument();
@ -591,7 +633,9 @@ nsresult ImageDocument::CheckOverflowing(bool changeState) {
if (changeState || mShouldResize || mFirstResize || windowBecameBigEnough ||
verticalOverflowChanged) {
if (ImageIsOverflowing() && (changeState || mShouldResize)) {
if (mIsInObjectOrEmbed) {
SetModeClass(eIsInObjectOrEmbed);
} else if (ImageIsOverflowing() && (changeState || mShouldResize)) {
ShrinkToFit();
} else if (mImageIsResized || mFirstResize || windowBecameBigEnough) {
RestoreImage();
@ -694,6 +738,37 @@ float ImageDocument::GetResolution() {
return mOriginalResolution;
}
void ImageDocument::MaybeSendResultToEmbedder(nsresult aResult) {
if (!mIsInObjectOrEmbed) {
return;
}
BrowsingContext* context = GetBrowsingContext();
if (!context) {
return;
}
if (context->GetParent() && context->GetParent()->IsInProcess()) {
if (Element* embedder = context->GetEmbedderElement()) {
if (nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent =
do_QueryInterface(embedder)) {
NS_DispatchToMainThread(NS_NewRunnableFunction(
"nsObjectLoadingContent::SubdocumentImageLoadComplete",
[objectLoadingContent, aResult]() {
static_cast<nsObjectLoadingContent*>(objectLoadingContent.get())
->SubdocumentImageLoadComplete(aResult);
}));
}
return;
}
}
if (BrowserChild* browserChild =
BrowserChild::GetFrom(context->GetDocShell())) {
browserChild->SendImageLoadComplete(aResult);
}
}
} // namespace mozilla::dom
nsresult NS_NewImageDocument(mozilla::dom::Document** aResult) {

Просмотреть файл

@ -8,11 +8,15 @@
#include "mozilla/Attributes.h"
#include "imgINotificationObserver.h"
#include "MediaDocument.h"
#include "mozilla/dom/MediaDocument.h"
#include "nsIDOMEventListener.h"
namespace mozilla::dom {
namespace mozilla {
enum class StyleImageRendering : uint8_t;
struct IntrinsicSize;
} // namespace mozilla
namespace mozilla::dom {
class HTMLImageElement;
class ImageDocument final : public MediaDocument,
@ -73,6 +77,8 @@ class ImageDocument final : public MediaDocument,
void NotifyPossibleTitleChange(bool aBoundTitleElement) override;
void UpdateRemoteStyle(StyleImageRendering aImageRendering);
protected:
virtual ~ImageDocument();
@ -98,7 +104,8 @@ class ImageDocument final : public MediaDocument,
eNone,
eShrinkToFit,
eOverflowingVertical, // And maybe horizontal too.
eOverflowingHorizontalOnly
eOverflowingHorizontalOnly,
eIsInObjectOrEmbed
};
void SetModeClass(eModeClasses mode);
@ -106,6 +113,8 @@ class ImageDocument final : public MediaDocument,
void OnLoadComplete(imgIRequest* aRequest, nsresult aStatus);
void OnHasTransparency();
void MaybeSendResultToEmbedder(nsresult aResult);
RefPtr<HTMLImageElement> mImageContent;
float mVisibleWidth;
@ -125,10 +134,23 @@ class ImageDocument final : public MediaDocument,
bool mTitleUpdateInProgress;
bool mHasCustomTitle;
// True iff embedder is either <object> or <embed>.
bool mIsInObjectOrEmbed;
float mOriginalZoomLevel;
float mOriginalResolution;
};
inline ImageDocument* Document::AsImageDocument() {
MOZ_ASSERT(IsImageDocument());
return static_cast<ImageDocument*>(this);
}
inline const ImageDocument* Document::AsImageDocument() const {
MOZ_ASSERT(IsImageDocument());
return static_cast<const ImageDocument*>(this);
}
} // namespace mozilla::dom
#endif /* mozilla_dom_ImageDocument_h */

Просмотреть файл

@ -114,6 +114,7 @@ EXPORTS.mozilla.dom += [
"HTMLUnknownElement.h",
"HTMLVideoElement.h",
"ImageDocument.h",
"MediaDocument.h",
"MediaError.h",
"nsBrowserElement.h",
"PlayPromise.h",

Просмотреть файл

@ -3,6 +3,7 @@ support-files =
bug592641_img.jpg
dummy_page.html
file_fullscreen-api-keys.html
image.png
submission_flush.html
post_action_page.html
form_data_file.bin
@ -82,6 +83,7 @@ support-files =
file_fullscreen-iframe-top.html
file_fullscreen-iframe-middle.html
file_fullscreen-iframe-inner.html
[browser_containerLoadingContent.js]
[browser_submission_flush.js]
[browser_refresh_after_document_write.js]
support-files =

Просмотреть файл

@ -0,0 +1,130 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const syntheticBrowsingContexts = SpecialPowers.getBoolPref(
"browser.opaqueResponseBlocking.syntheticBrowsingContext",
false
);
const DIRPATH = getRootDirectory(gTestPath).replace(
"chrome://mochitests/content/",
""
);
const ORIGIN = "https://example.com";
const CROSSORIGIN = "https://example.org";
const TABURL = `${ORIGIN}/${DIRPATH}dummy_page.html`;
const IMAGEURL = `${ORIGIN}/${DIRPATH}image.png`;
const CROSSIMAGEURL = `${CROSSORIGIN}/${DIRPATH}image.png`;
const DOCUMENTURL = `${ORIGIN}/${DIRPATH}dummy_page.html`;
const CROSSDOCUMENTURL = `${CROSSORIGIN}/${DIRPATH}dummy_page.html`;
async function createElements({ element, attribute }, url1, url2) {
for (let url of [url1, url2]) {
const object = content.document.createElement(element);
object[attribute] = url;
const onloadPromise = new Promise(res => {
object.onload = res;
});
content.document.body.appendChild(object);
await onloadPromise;
return object;
}
}
function getPids(browser) {
return browser.browsingContext.children.map(
child => child.currentWindowContext.osPid
);
}
async function runTest(spec, tabUrl, imageurl, crossimageurl, check) {
await BrowserTestUtils.withNewTab(tabUrl, async browser => {
await SpecialPowers.spawn(
browser,
[spec, imageurl, crossimageurl],
async ({ element, attribute }, url1, url2) => {
for (let url of [url1, url2]) {
const object = content.document.createElement(element);
object[attribute] = url;
const onloadPromise = new Promise(res => {
object.onload = res;
});
content.document.body.appendChild(object);
await onloadPromise;
}
}
);
await check(browser);
});
}
let iframe = { element: "iframe", attribute: "src" };
let embed = { element: "embed", attribute: "src" };
let object = { element: "object", attribute: "data" };
async function checkImage(browser) {
let pids = getPids(browser);
is(pids.length, 2, "There should be two browsing contexts");
ok(pids[0], "The first pid should have a sane value");
ok(pids[1], "The second pid should have a sane value");
isnot(pids[0], pids[1], "The two pids should be different");
let images = [];
for (let context of browser.browsingContext.children) {
images.push(
await SpecialPowers.spawn(context, [], async () => {
let img = new URL(content.document.querySelector("img").src);
is(
`${img.protocol}//${img.host}`,
`${content.location.protocol}//${content.location.host}`,
"Images should be loaded in the same domain as the document"
);
return img.href;
})
);
}
isnot(images[0], images[1], "The images should have different sources");
}
function checkDocument(browser) {
let pids = getPids(browser);
is(pids.length, 2, "There should be two browsing contexts");
ok(pids[0], "The first pid should have a sane value");
ok(pids[1], "The second pid should have a sane value");
isnot(pids[0], pids[1], "The two pids should be different");
}
add_task(async function test_iframeImageDocument() {
await runTest(iframe, TABURL, IMAGEURL, CROSSIMAGEURL, checkImage);
});
if (syntheticBrowsingContexts) {
add_task(async function test_embedImageDocument() {
await runTest(embed, TABURL, IMAGEURL, CROSSIMAGEURL, checkImage);
});
add_task(async function test_objectImageDocument() {
await runTest(object, TABURL, IMAGEURL, CROSSIMAGEURL, checkImage);
});
}
add_task(async function test_iframeDocument() {
await runTest(iframe, TABURL, DOCUMENTURL, CROSSDOCUMENTURL, checkDocument);
});
if (syntheticBrowsingContexts) {
add_task(async function test_embedDocument() {
await runTest(embed, TABURL, DOCUMENTURL, CROSSDOCUMENTURL, checkDocument);
});
add_task(async function test_objectDocument() {
await runTest(object, TABURL, DOCUMENTURL, CROSSDOCUMENTURL, checkDocument);
});
}

Просмотреть файл

@ -623,3 +623,4 @@ skip-if = toolkit == "android" # test does not function on android due to aggre
support-files =
file_window_close_and_open.html
file_broadcast_load.html
[test_frame_count_with_synthetic_doc.html]

Просмотреть файл

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<script>
SimpleTest.waitForExplicitFinish();
function getWindowLength() {
setTimeout(function() {
if (window.length > 0) {
ok(false, "Synthetic document shouldn't be exposed");
}
// Keep running this check until the test stops
getWindowLength();
});
}
function addObject() {
const object = document.createElement("object");
object.data = 'file_bug417760.png';
document.body.appendChild(object);
object.onload = function() {
ok(true, "Test passes");
SimpleTest.finish();
}
}
getWindowLength();
addObject();
</script>
</body>
</html>

Просмотреть файл

@ -251,4 +251,15 @@ mozilla::ipc::IPCResult BrowserBridgeChild::RecvIntrinsicSizeOrRatioChanged(
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserBridgeChild::RecvImageLoadComplete(
const nsresult& aResult) {
if (RefPtr<Element> owner = mFrameLoader->GetOwnerContent()) {
if (nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(owner)) {
static_cast<nsObjectLoadingContent*>(olc.get())
->SubdocumentImageLoadComplete(aResult);
}
}
return IPC_OK();
}
} // namespace mozilla::dom

Просмотреть файл

@ -96,6 +96,8 @@ class BrowserBridgeChild : public PBrowserBridgeChild {
const Maybe<IntrinsicSize>& aIntrinsicSize,
const Maybe<AspectRatio>& aIntrinsicRatio);
mozilla::ipc::IPCResult RecvImageLoadComplete(const nsresult& aResult);
MOZ_CAN_RUN_SCRIPT_BOUNDARY
mozilla::ipc::IPCResult RecvScrollRectIntoView(
const nsRect& aRect, const ScrollAxis& aVertical,

Просмотреть файл

@ -257,6 +257,12 @@ IPCResult BrowserBridgeParent::RecvSetIsUnderHiddenEmbedderElement(
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserBridgeParent::RecvUpdateRemoteStyle(
const StyleImageRendering& aImageRendering) {
Unused << mBrowserParent->SendUpdateRemoteStyle(aImageRendering);
return IPC_OK();
}
#ifdef ACCESSIBILITY
a11y::DocAccessibleParent* BrowserBridgeParent::GetDocAccessibleParent() {
auto* embeddedBrowser = GetBrowserParent();

Просмотреть файл

@ -101,6 +101,9 @@ class BrowserBridgeParent : public PBrowserBridgeParent {
mozilla::ipc::IPCResult RecvSetIsUnderHiddenEmbedderElement(
const bool& aIsUnderHiddenEmbedderElement);
mozilla::ipc::IPCResult RecvUpdateRemoteStyle(
const StyleImageRendering& aImageRendering);
#ifdef ACCESSIBILITY
mozilla::ipc::IPCResult RecvSetEmbedderAccessible(PDocAccessibleParent* aDoc,
uint64_t aID);

Просмотреть файл

@ -62,7 +62,9 @@
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/JSWindowActorChild.h"
#include "mozilla/dom/ImageDocument.h"
#include "mozilla/dom/LoadURIOptionsBinding.h"
#include "mozilla/dom/MediaDocument.h"
#include "mozilla/dom/MessageManagerBinding.h"
#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/dom/Nullable.h"
@ -1328,6 +1330,25 @@ mozilla::ipc::IPCResult BrowserChild::RecvSetIsUnderHiddenEmbedderElement(
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvUpdateRemoteStyle(
const StyleImageRendering& aImageRendering) {
BrowsingContext* context = GetBrowsingContext();
if (!context) {
return IPC_OK();
}
Document* document = context->GetDocument();
if (!document) {
return IPC_OK();
}
if (document->IsImageDocument()) {
document->AsImageDocument()->UpdateRemoteStyle(aImageRendering);
}
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserChild::RecvDynamicToolbarMaxHeightChanged(
const ScreenIntCoord& aHeight) {
#if defined(MOZ_WIDGET_ANDROID)

Просмотреть файл

@ -416,6 +416,9 @@ class BrowserChild final : public nsMessageManagerScriptExecutor,
mozilla::ipc::IPCResult RecvInsertText(const nsAString& aStringToInsert);
mozilla::ipc::IPCResult RecvUpdateRemoteStyle(
const StyleImageRendering& aImageRendering);
mozilla::ipc::IPCResult RecvNormalPriorityInsertText(
const nsAString& aStringToInsert);

Просмотреть файл

@ -3120,6 +3120,18 @@ mozilla::ipc::IPCResult BrowserParent::RecvIntrinsicSizeOrRatioChanged(
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserParent::RecvImageLoadComplete(
const nsresult& aResult) {
BrowserBridgeParent* bridge = GetBrowserBridgeParent();
if (!bridge || !bridge->CanSend()) {
return IPC_OK();
}
Unused << bridge->SendImageLoadComplete(aResult);
return IPC_OK();
}
bool BrowserParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent) {
nsCOMPtr<nsIWidget> textInputHandlingWidget = GetTextInputHandlingWidget();
if (!textInputHandlingWidget) {

Просмотреть файл

@ -324,6 +324,8 @@ class BrowserParent final : public PBrowserParent,
const Maybe<IntrinsicSize>& aIntrinsicSize,
const Maybe<AspectRatio>& aIntrinsicRatio);
mozilla::ipc::IPCResult RecvImageLoadComplete(const nsresult& aResult);
mozilla::ipc::IPCResult RecvSyncMessage(
const nsString& aMessage, const ClonedMessageData& aData,
nsTArray<ipc::StructuredCloneData>* aRetVal);

Просмотреть файл

@ -106,6 +106,7 @@ using mozilla::dom::EmbedderElementEventType from "mozilla/dom/TabMessageTypes.h
using mozilla::IntrinsicSize from "nsIFrame.h";
using mozilla::AspectRatio from "mozilla/AspectRatio.h";
using mozilla::NativeKeyBindingsType from "mozilla/NativeKeyBindingsType.h";
using mozilla::StyleImageRendering from "mozilla/ServoStyleConsts.h";
namespace mozilla {
namespace dom {
@ -574,6 +575,8 @@ parent:
async IntrinsicSizeOrRatioChanged(IntrinsicSize? aIntrinsicSize,
AspectRatio? aIntrinsicRatio);
async ImageLoadComplete(nsresult aResult);
/**
* Child informs the parent that a pointer lock has requested/released.
*/
@ -711,6 +714,8 @@ child:
async SetIsUnderHiddenEmbedderElement(bool aIsUnderHiddenEmbedderElement);
async UpdateRemoteStyle(StyleImageRendering aImageRendering);
async DynamicToolbarMaxHeightChanged(ScreenIntCoord height);
async DynamicToolbarOffsetChanged(ScreenIntCoord height);

Просмотреть файл

@ -30,6 +30,7 @@ using mozilla::dom::EmbedderElementEventType from "mozilla/dom/TabMessageTypes.h
[RefCounted] using class nsDocShellLoadState from "nsDocShellLoadState.h";
using mozilla::IntrinsicSize from "nsIFrame.h";
using mozilla::AspectRatio from "mozilla/AspectRatio.h";
using mozilla::StyleImageRendering from "mozilla/ServoStyleConsts.h";
namespace mozilla {
namespace dom {
@ -78,6 +79,8 @@ child:
async IntrinsicSizeOrRatioChanged(IntrinsicSize? aIntrinsicSize,
AspectRatio? aIntrinsicRatio);
async ImageLoadComplete(nsresult aResult);
both:
// Destroy the remote web browser due to the nsFrameLoader going away.
@ -121,6 +124,8 @@ parent:
async SetIsUnderHiddenEmbedderElement(bool aIsUnderHiddenEmbedderElement);
async UpdateRemoteStyle(StyleImageRendering aImageRendering);
async WillChangeProcess();
#ifdef ACCESSIBILITY

Просмотреть файл

@ -34,6 +34,7 @@
#include "mozilla/ScopeExit.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/ServoStyleSetInlines.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/StaticPrefs_mathml.h"
#include "mozilla/Unused.h"
@ -3610,6 +3611,14 @@ nsCSSFrameConstructor::FindInputData(const Element& aElement,
ArrayLength(sInputData));
}
static nsIFrame* NS_NewSubDocumentOrImageFrame(mozilla::PresShell* aPresShell,
mozilla::ComputedStyle* aStyle) {
return StaticPrefs::
browser_opaqueResponseBlocking_syntheticBrowsingContext_AtStartup()
? NS_NewSubDocumentFrame(aPresShell, aStyle)
: NS_NewImageFrame(aPresShell, aStyle);
}
/* static */
const nsCSSFrameConstructor::FrameConstructionData*
nsCSSFrameConstructor::FindObjectData(const Element& aElement,
@ -3640,7 +3649,8 @@ nsCSSFrameConstructor::FindObjectData(const Element& aElement,
NS_NewEmptyFrame),
SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_FALLBACK,
ToCreationFunc(NS_NewBlockFrame)),
SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_IMAGE, NS_NewImageFrame),
SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_IMAGE,
NS_NewSubDocumentOrImageFrame),
SIMPLE_INT_CREATE(nsIObjectLoadingContent::TYPE_DOCUMENT,
NS_NewSubDocumentFrame),
// Fake plugin handlers load as documents

Просмотреть файл

@ -117,10 +117,12 @@ nsContentDLF::CreateInstance(const char* aCommand, nsIChannel* aChannel,
contentType = TEXT_PLAIN;
}
nsresult rv;
bool imageDocument = false;
// Try html or plaintext; both use the same document CID
if (IsTypeInList(contentType, gHTMLTypes) ||
nsContentUtils::IsPlainTextType(contentType)) {
return CreateDocument(
rv = CreateDocument(
aCommand, aChannel, aLoadGroup, aContainer,
[]() -> already_AddRefed<Document> {
RefPtr<Document> doc;
@ -129,11 +131,9 @@ nsContentDLF::CreateInstance(const char* aCommand, nsIChannel* aChannel,
return doc.forget();
},
aDocListener, aDocViewer);
}
// Try XML
if (IsTypeInList(contentType, gXMLTypes)) {
return CreateDocument(
} // Try XML
else if (IsTypeInList(contentType, gXMLTypes)) {
rv = CreateDocument(
aCommand, aChannel, aLoadGroup, aContainer,
[]() -> already_AddRefed<Document> {
RefPtr<Document> doc;
@ -142,11 +142,9 @@ nsContentDLF::CreateInstance(const char* aCommand, nsIChannel* aChannel,
return doc.forget();
},
aDocListener, aDocViewer);
}
// Try SVG
if (IsTypeInList(contentType, gSVGTypes)) {
return CreateDocument(
} // Try SVG
else if (IsTypeInList(contentType, gSVGTypes)) {
rv = CreateDocument(
aCommand, aChannel, aLoadGroup, aContainer,
[]() -> already_AddRefed<Document> {
RefPtr<Document> doc;
@ -155,12 +153,10 @@ nsContentDLF::CreateInstance(const char* aCommand, nsIChannel* aChannel,
return doc.forget();
},
aDocListener, aDocViewer);
}
if (mozilla::DecoderTraits::ShouldHandleMediaType(
contentType.get(),
/* DecoderDoctorDiagnostics* */ nullptr)) {
return CreateDocument(
} else if (mozilla::DecoderTraits::ShouldHandleMediaType(
contentType.get(),
/* DecoderDoctorDiagnostics* */ nullptr)) {
rv = CreateDocument(
aCommand, aChannel, aLoadGroup, aContainer,
[]() -> already_AddRefed<Document> {
RefPtr<Document> doc;
@ -169,11 +165,10 @@ nsContentDLF::CreateInstance(const char* aCommand, nsIChannel* aChannel,
return doc.forget();
},
aDocListener, aDocViewer);
}
// Try image types
if (IsImageContentType(contentType)) {
return CreateDocument(
} // Try image types
else if (IsImageContentType(contentType)) {
imageDocument = true;
rv = CreateDocument(
aCommand, aChannel, aLoadGroup, aContainer,
[]() -> already_AddRefed<Document> {
RefPtr<Document> doc;
@ -182,10 +177,18 @@ nsContentDLF::CreateInstance(const char* aCommand, nsIChannel* aChannel,
return doc.forget();
},
aDocListener, aDocViewer);
} else {
// If we get here, then we weren't able to create anything. Sorry!
return NS_ERROR_FAILURE;
}
// If we get here, then we weren't able to create anything. Sorry!
return NS_ERROR_FAILURE;
if (NS_SUCCEEDED(rv) && !imageDocument) {
Document* doc = (*aDocViewer)->GetDocument();
MOZ_ASSERT(doc);
doc->MakeBrowsingContextNonSynthetic();
}
return rv;
}
NS_IMETHODIMP

Просмотреть файл

@ -7,9 +7,11 @@
#ifndef LAYOUT_GENERIC_LAYOUTMESSAGEUTILS_H_
#define LAYOUT_GENERIC_LAYOUTMESSAGEUTILS_H_
#include "ipc/EnumSerializer.h"
#include "ipc/IPCMessageUtils.h"
#include "nsIFrame.h"
#include "mozilla/AspectRatio.h"
#include "mozilla/webrender/WebRenderTypes.h"
namespace IPC {
@ -41,6 +43,12 @@ struct ParamTraits<mozilla::AspectRatio> {
}
};
template <>
struct ParamTraits<mozilla::StyleImageRendering>
: public ContiguousEnumSerializerInclusive<
mozilla::StyleImageRendering, mozilla::StyleImageRendering::Auto,
mozilla::StyleImageRendering::Optimizequality> {};
} // namespace IPC
#endif // LAYOUT_GENERIC_LAYOUTMESSAGEUTILS_H_

Просмотреть файл

@ -42,6 +42,7 @@
#include "nsIImageLoadingContent.h"
#include "nsImageLoadingContent.h"
#include "nsImageRenderer.h"
#include "nsObjectLoadingContent.h"
#include "nsString.h"
#include "nsPrintfCString.h"
#include "nsPresContext.h"
@ -355,6 +356,8 @@ void nsImageFrame::DisconnectMap() {
void nsImageFrame::DestroyFrom(nsIFrame* aDestructRoot,
PostDestroyData& aPostDestroyData) {
MaybeSendIntrinsicSizeAndRatioToEmbedder(Nothing(), Nothing());
if (mReflowCallbackPosted) {
PresShell()->CancelReflowCallback(this);
mReflowCallbackPosted = false;
@ -513,6 +516,11 @@ void nsImageFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
// We have a PresContext now, so we need to notify the image content node
// that it can register images.
imageLoader->FrameCreated(this);
if (nsIDocShell* docShell = PresContext()->GetDocShell()) {
RefPtr<BrowsingContext> bc = docShell->GetBrowsingContext();
mIsInObjectOrEmbed = bc->IsEmbedderTypeObjectOrEmbed() &&
PresContext()->Document()->IsImageDocument();
}
} else {
const StyleImage* image = GetImageFromStyle();
MOZ_ASSERT(mKind == Kind::ListStyleImage || image->IsImageRequestType(),
@ -538,6 +546,8 @@ void nsImageFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
currentRequest->BoostPriority(categoryToBoostPriority);
}
MaybeSendIntrinsicSizeAndRatioToEmbedder();
}
void nsImageFrame::SetupForContentURLRequest() {
@ -950,6 +960,13 @@ void nsImageFrame::UpdateImage(imgIRequest* aRequest, imgIContainer* aImage) {
bool intrinsicRatioChanged = UpdateIntrinsicRatio();
return intrinsicSizeChanged || intrinsicRatioChanged;
}();
if (intrinsicSizeOrRatioChanged) {
// Our aspect-ratio property value changed, and an embedding <object> or
// <embed> might care about that.
MaybeSendIntrinsicSizeAndRatioToEmbedder();
}
if (!GotInitialReflow()) {
return;
}
@ -1022,6 +1039,47 @@ void nsImageFrame::InvalidateSelf(const nsIntRect* aLayerInvalidRect,
}
}
void nsImageFrame::MaybeSendIntrinsicSizeAndRatioToEmbedder() {
MaybeSendIntrinsicSizeAndRatioToEmbedder(Some(GetIntrinsicSize()),
Some(GetAspectRatio()));
}
void nsImageFrame::MaybeSendIntrinsicSizeAndRatioToEmbedder(
Maybe<IntrinsicSize> aIntrinsicSize, Maybe<AspectRatio> aIntrinsicRatio) {
if (!mIsInObjectOrEmbed || !mImage) {
return;
}
nsCOMPtr<nsIDocShell> docShell = PresContext()->GetDocShell();
if (!docShell) {
return;
}
BrowsingContext* bc = docShell->GetBrowsingContext();
if (!bc) {
return;
}
MOZ_ASSERT(bc->IsContentSubframe());
if (bc->GetParent()->IsInProcess()) {
if (Element* embedder = bc->GetEmbedderElement()) {
if (nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(embedder)) {
static_cast<nsObjectLoadingContent*>(olc.get())
->SubdocumentIntrinsicSizeOrRatioChanged(aIntrinsicSize,
aIntrinsicRatio);
} else {
MOZ_ASSERT_UNREACHABLE("Got out of sync?");
}
return;
}
}
if (BrowserChild* browserChild = BrowserChild::GetFrom(docShell)) {
Unused << browserChild->SendIntrinsicSizeOrRatioChanged(aIntrinsicSize,
aIntrinsicRatio);
}
}
void nsImageFrame::OnLoadComplete(imgIRequest* aRequest, nsresult aStatus) {
NotifyNewCurrentRequest(aRequest, aStatus);
}
@ -1039,6 +1097,10 @@ void nsImageFrame::ResponsiveContentDensityChanged() {
return;
}
// Our aspect-ratio property value changed, and an embedding <object> or
// <embed> might care about that.
MaybeSendIntrinsicSizeAndRatioToEmbedder();
PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange,
NS_FRAME_IS_DIRTY);
}
@ -1151,8 +1213,15 @@ void nsImageFrame::EnsureIntrinsicSizeAndRatio() {
return;
}
UpdateIntrinsicSize();
UpdateIntrinsicRatio();
bool intrinsicSizeOrRatioChanged = UpdateIntrinsicSize();
intrinsicSizeOrRatioChanged =
UpdateIntrinsicRatio() || intrinsicSizeOrRatioChanged;
if (intrinsicSizeOrRatioChanged) {
// Our aspect-ratio property value changed, and an embedding <object> or
// <embed> might care about that.
MaybeSendIntrinsicSizeAndRatioToEmbedder();
}
}
nsIFrame::SizeComputationResult nsImageFrame::ComputeSize(

Просмотреть файл

@ -370,6 +370,10 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback {
void InvalidateSelf(const nsIntRect* aLayerInvalidRect,
const nsRect* aFrameInvalidRect);
void MaybeSendIntrinsicSizeAndRatioToEmbedder();
void MaybeSendIntrinsicSizeAndRatioToEmbedder(Maybe<mozilla::IntrinsicSize>,
Maybe<mozilla::AspectRatio>);
RefPtr<nsImageMap> mImageMap;
RefPtr<nsImageListener> mListener;
@ -397,6 +401,8 @@ class nsImageFrame : public nsAtomicContainerFrame, public nsIReflowCallback {
bool mReflowCallbackPosted;
bool mForceSyncDecoding;
bool mIsInObjectOrEmbed = false;
/* loading / broken image icon support */
// XXXbz this should be handled by the prescontext, I think; that

Просмотреть файл

@ -17,6 +17,7 @@
#include "mozilla/Unused.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/HTMLFrameElement.h"
#include "mozilla/dom/ImageDocument.h"
#include "mozilla/dom/BrowserParent.h"
#include "nsCOMPtr.h"
@ -73,7 +74,8 @@ nsSubDocumentFrame::nsSubDocumentFrame(ComputedStyle* aStyle,
mIsInline(false),
mPostedReflowCallback(false),
mDidCreateDoc(false),
mCallingShow(false) {}
mCallingShow(false),
mIsInObjectOrEmbed(false) {}
#ifdef ACCESSIBILITY
a11y::AccType nsSubDocumentFrame::AccessibleType() {
@ -145,8 +147,14 @@ void nsSubDocumentFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
frameloader->Hide();
}
}
if (RefPtr<BrowsingContext> bc = frameloader->GetExtantBrowsingContext()) {
mIsInObjectOrEmbed = bc->IsEmbedderTypeObjectOrEmbed();
}
}
MaybeUpdateRemoteStyle();
PropagateIsUnderHiddenEmbedderElementToSubView(
PresShell()->IsUnderHiddenEmbedderElement() ||
!StyleVisibility()->IsVisible());
@ -181,7 +189,7 @@ void nsSubDocumentFrame::ShowViewer() {
}
RefPtr<nsFrameLoader> frameloader = FrameLoader();
if (!frameloader) {
if (!frameloader || frameloader->IsDead()) {
return;
}
@ -845,11 +853,51 @@ void nsSubDocumentFrame::MaybeUpdateEmbedderColorScheme() {
Unused << bc->SetEmbedderColorScheme(value);
}
void nsSubDocumentFrame::MaybeUpdateRemoteStyle(
ComputedStyle* aOldComputedStyle) {
if (!mIsInObjectOrEmbed) {
return;
}
if (aOldComputedStyle &&
aOldComputedStyle->StyleVisibility()->mImageRendering ==
Style()->StyleVisibility()->mImageRendering) {
return;
}
if (!mFrameLoader) {
return;
}
if (mFrameLoader->IsRemoteFrame()) {
mFrameLoader->UpdateRemoteStyle(
Style()->StyleVisibility()->mImageRendering);
return;
}
BrowsingContext* context = mFrameLoader->GetExtantBrowsingContext();
if (!context) {
return;
}
Document* document = context->GetDocument();
if (!document) {
return;
}
if (document->IsImageDocument()) {
document->AsImageDocument()->UpdateRemoteStyle(
Style()->StyleVisibility()->mImageRendering);
}
}
void nsSubDocumentFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
nsAtomicContainerFrame::DidSetComputedStyle(aOldComputedStyle);
MaybeUpdateEmbedderColorScheme();
MaybeUpdateRemoteStyle(aOldComputedStyle);
// If this presshell has invisible ancestors, we don't need to propagate the
// visibility style change to the subdocument since the subdocument should
// have already set the IsUnderHiddenEmbedderElement flag in
@ -1235,11 +1283,6 @@ nsPoint nsSubDocumentFrame::GetExtraOffset() const {
}
void nsSubDocumentFrame::SubdocumentIntrinsicSizeOrRatioChanged() {
if (MOZ_UNLIKELY(HasAllStateBits(NS_FRAME_IS_DIRTY))) {
// We will be reflowed soon anyway.
return;
}
const nsStylePosition* pos = StylePosition();
bool dependsOnIntrinsics =
!pos->mWidth.ConvertsToLength() || !pos->mHeight.ConvertsToLength();
@ -1248,6 +1291,7 @@ void nsSubDocumentFrame::SubdocumentIntrinsicSizeOrRatioChanged() {
auto dirtyHint = dependsOnIntrinsics ? IntrinsicDirty::StyleChange
: IntrinsicDirty::Resize;
PresShell()->FrameNeedsReflow(this, dirtyHint, NS_FRAME_IS_DIRTY);
InvalidateFrame();
}
}

Просмотреть файл

@ -135,6 +135,8 @@ class nsSubDocumentFrame final : public nsAtomicContainerFrame,
void ClearRetainedPaintData();
void MaybeUpdateEmbedderColorScheme();
void MaybeUpdateRemoteStyle(ComputedStyle* aOldComputedStyle = nullptr);
void PropagateIsUnderHiddenEmbedderElementToSubView(
bool aIsUnderHiddenEmbedderElement);
@ -193,6 +195,7 @@ class nsSubDocumentFrame final : public nsAtomicContainerFrame,
bool mPostedReflowCallback : 1;
bool mDidCreateDoc : 1;
bool mCallingShow : 1;
bool mIsInObjectOrEmbed : 1;
};
namespace mozilla {

Просмотреть файл

@ -25,6 +25,11 @@ body {
}
}
.isInObjectOrEmbed {
width: 100%;
height: 100vh;
}
img {
display: block;
}

Просмотреть файл

@ -1617,6 +1617,23 @@
value: @IS_NIGHTLY_BUILD@
mirror: always
# When this pref is enabled, <object> and <embed> elements will create
# synthetic documents when the resource type they're loading is an image.
- name: browser.opaqueResponseBlocking.syntheticBrowsingContext
type: bool
value: @IS_NIGHTLY_BUILD@
mirror: once
# When this pref is enabled, <object> and <embed> elements will filter the
# browsing contexts created for the synthetic browsing contexts for the
# synthetic documents when browser.opaqueResponseBlocking.syntheticBrowsingContext
# is enabled from `Window.frames`, `Window.length` and named targeting.
- name: browser.opaqueResponseBlocking.syntheticBrowsingContext.filter
type: bool
value: @IS_NIGHTLY_BUILD@
mirror: once
do_not_use_directly: true
# When true, zooming will be enabled on all sites, even ones that declare
# user-scalable=no.
- name: browser.ui.zoom.force-user-scalable