Bug 1104916 - Implement CSS media query display-mode. r=cam

This commit is contained in:
Brendan Dahl 2016-02-23 17:10:00 +01:00
parent a4b065f6fe
commit f20e4d1ec0
16 changed files with 284 additions and 12 deletions

View File

@ -308,6 +308,7 @@ GK_ATOM(disableOutputEscaping, "disable-output-escaping")
GK_ATOM(disabled, "disabled")
GK_ATOM(disablehistory, "disablehistory")
GK_ATOM(display, "display")
GK_ATOM(displayMode, "display-mode")
GK_ATOM(distinct, "distinct")
GK_ATOM(div, "div")
GK_ATOM(dl, "dl")

View File

@ -520,7 +520,8 @@ child:
TextureFactoryIdentifier textureFactoryIdentifier,
uint64_t layersId,
nullable PRenderFrame renderFrame,
bool parentIsActive);
bool parentIsActive,
nsSizeMode sizeMode);
async LoadURL(nsCString uri, BrowserConfiguration config, ShowInfo info);
@ -528,10 +529,12 @@ child:
async CacheFileDescriptor(nsString path, FileDescriptor fd);
async UpdateDimensions(CSSRect rect, CSSSize size, nsSizeMode sizeMode,
async UpdateDimensions(CSSRect rect, CSSSize size,
ScreenOrientationInternal orientation,
LayoutDeviceIntPoint chromeDisp) compressall;
async SizeModeChanged(nsSizeMode sizeMode);
/**
* Sending an activate message moves focus to the child.
*/

View File

@ -1482,7 +1482,7 @@ TabChild::DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
PRenderFrameChild* aRenderFrame, const ShowInfo& aShowInfo)
{
RecvShow(ScreenIntSize(0, 0), aShowInfo, aTextureFactoryIdentifier,
aLayersId, aRenderFrame, mParentIsActive);
aLayersId, aRenderFrame, mParentIsActive, nsSizeMode_Normal);
mDidFakeShow = true;
}
@ -1594,10 +1594,12 @@ TabChild::RecvShow(const ScreenIntSize& aSize,
const TextureFactoryIdentifier& aTextureFactoryIdentifier,
const uint64_t& aLayersId,
PRenderFrameChild* aRenderFrame,
const bool& aParentIsActive)
const bool& aParentIsActive,
const nsSizeMode& aSizeMode)
{
MOZ_ASSERT((!mDidFakeShow && aRenderFrame) || (mDidFakeShow && !aRenderFrame));
mPuppetWidget->SetSizeMode(aSizeMode);
if (mDidFakeShow) {
ApplyShowInfo(aInfo);
RecvParentActivated(aParentIsActive);
@ -1633,7 +1635,6 @@ TabChild::RecvShow(const ScreenIntSize& aSize,
bool
TabChild::RecvUpdateDimensions(const CSSRect& rect, const CSSSize& size,
const nsSizeMode& sizeMode,
const ScreenOrientationInternal& orientation,
const LayoutDeviceIntPoint& chromeDisp)
{
@ -1660,7 +1661,6 @@ TabChild::RecvUpdateDimensions(const CSSRect& rect, const CSSSize& size,
baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height,
true);
mPuppetWidget->SetSizeMode(sizeMode);
mPuppetWidget->Resize(screenRect.x + chromeDisp.x,
screenRect.y + chromeDisp.y,
screenSize.width, screenSize.height, true);
@ -1668,6 +1668,22 @@ TabChild::RecvUpdateDimensions(const CSSRect& rect, const CSSSize& size,
return true;
}
bool
TabChild::RecvSizeModeChanged(const nsSizeMode& aSizeMode)
{
mPuppetWidget->SetSizeMode(aSizeMode);
nsCOMPtr<nsIDocument> document(GetDocument());
nsCOMPtr<nsIPresShell> presShell = document->GetShell();
if (presShell) {
nsPresContext* presContext = presShell->GetPresContext();
if (presContext) {
presContext->MediaFeatureValuesChangedAllDocuments(eRestyle_Subtree,
NS_STYLE_HINT_REFLOW);
}
}
return true;
}
bool
TabChild::UpdateFrame(const FrameMetrics& aFrameMetrics)
{

View File

@ -326,14 +326,16 @@ public:
const TextureFactoryIdentifier& aTextureFactoryIdentifier,
const uint64_t& aLayersId,
PRenderFrameChild* aRenderFrame,
const bool& aParentIsActive) override;
const bool& aParentIsActive,
const nsSizeMode& aSizeMode) override;
virtual bool
RecvUpdateDimensions(const CSSRect& aRect,
const CSSSize& aSize,
const nsSizeMode& aSizeMode,
const ScreenOrientationInternal& aOrientation,
const LayoutDeviceIntPoint& aChromeDisp) override;
virtual bool
RecvSizeModeChanged(const nsSizeMode& aSizeMode) override;
virtual bool RecvActivate() override;

View File

@ -279,6 +279,7 @@ TabParent::TabParent(nsIContentParent* aManager,
, mDPI(0)
, mDefaultScale(0)
, mUpdatedDimensions(false)
, mSizeMode(nsSizeMode_Normal)
, mManager(aManager)
, mDocShellIsActive(false)
, mMarkedDestroying(false)
@ -894,8 +895,14 @@ TabParent::Show(const ScreenIntSize& size, bool aParentIsActive)
}
}
nsCOMPtr<nsISupports> container = mFrameElement->OwnerDoc()->GetContainer();
nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
nsCOMPtr<nsIWidget> mainWidget;
baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
mSizeMode = mainWidget ? mainWidget->SizeMode() : nsSizeMode_Normal;
Unused << SendShow(size, GetShowInfo(), textureFactoryIdentifier,
layersId, renderFrame, aParentIsActive);
layersId, renderFrame, aParentIsActive, mSizeMode);
}
bool
@ -906,7 +913,6 @@ TabParent::RecvSetDimensions(const uint32_t& aFlags,
MOZ_ASSERT(!(aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_INNER),
"We should never see DIM_FLAGS_SIZE_INNER here!");
nsCOMPtr<nsIWidget> widget = GetWidget();
NS_ENSURE_TRUE(mFrameElement, true);
nsCOMPtr<nsIDocShell> docShell = mFrameElement->OwnerDoc()->GetDocShell();
NS_ENSURE_TRUE(docShell, true);
@ -990,11 +996,19 @@ TabParent::UpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size)
CSSRect unscaledRect = devicePixelRect / widgetScale;
CSSSize unscaledSize = devicePixelSize / widgetScale;
Unused << SendUpdateDimensions(unscaledRect, unscaledSize,
widget->SizeMode(),
orientation, chromeOffset);
}
}
void
TabParent::SizeModeChanged(const nsSizeMode& aSizeMode)
{
if (!mIsDestroyed && aSizeMode != mSizeMode) {
mSizeMode = aSizeMode;
Unused << SendSizeModeChanged(aSizeMode);
}
}
void
TabParent::UIResolutionChanged()
{

View File

@ -337,6 +337,8 @@ public:
void UpdateDimensions(const nsIntRect& aRect, const ScreenIntSize& aSize);
void SizeModeChanged(const nsSizeMode& aSizeMode);
void UIResolutionChanged();
void ThemeChanged();
@ -600,6 +602,7 @@ protected:
float mDPI;
CSSToLayoutDeviceScale mDefaultScale;
bool mUpdatedDimensions;
nsSizeMode mSizeMode;
LayoutDeviceIntPoint mChromeOffset;
private:

View File

@ -2051,6 +2051,39 @@ nsPresContext::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
RestyleManager()->PostRebuildAllStyleDataEvent(aExtraHint, aRestyleHint);
}
struct MediaFeatureHints
{
nsRestyleHint restyleHint;
nsChangeHint changeHint;
};
static bool
MediaFeatureValuesChangedAllDocumentsCallback(nsIDocument* aDocument, void* aHints)
{
MediaFeatureHints* hints = static_cast<MediaFeatureHints*>(aHints);
if (nsIPresShell* shell = aDocument->GetShell()) {
if (nsPresContext* pc = shell->GetPresContext()) {
pc->MediaFeatureValuesChangedAllDocuments(hints->restyleHint,
hints->changeHint);
}
}
return true;
}
void
nsPresContext::MediaFeatureValuesChangedAllDocuments(nsRestyleHint aRestyleHint,
nsChangeHint aChangeHint)
{
MediaFeatureValuesChanged(aRestyleHint, aChangeHint);
MediaFeatureHints hints = {
aRestyleHint,
aChangeHint
};
mDocument->EnumerateSubDocuments(MediaFeatureValuesChangedAllDocumentsCallback,
&hints);
}
void
nsPresContext::MediaFeatureValuesChanged(nsRestyleHint aRestyleHint,
nsChangeHint aChangeHint)
@ -2157,6 +2190,25 @@ nsPresContext::HandleMediaFeatureValuesChangedEvent()
}
}
static void
NotifyTabSizeModeChanged(TabParent* aTab, void* aArg)
{
nsSizeMode* sizeMode = static_cast<nsSizeMode*>(aArg);
aTab->SizeModeChanged(*sizeMode);
}
void
nsPresContext::SizeModeChanged(nsSizeMode aSizeMode)
{
if (HasCachedStyleData()) {
nsContentUtils::CallOnAllRemoteChildren(mDocument->GetWindow(),
NotifyTabSizeModeChanged,
&aSizeMode);
MediaFeatureValuesChangedAllDocuments(eRestyle_Subtree,
NS_STYLE_HINT_REFLOW);
}
}
nsCompatibility
nsPresContext::CompatibilityMode() const
{

View File

@ -21,6 +21,7 @@
#include "nsIObserver.h"
#include "nsITimer.h"
#include "nsCRT.h"
#include "nsIWidgetListener.h"
#include "FramePropertyTable.h"
#include "nsGkAtoms.h"
#include "nsCycleCollectionParticipant.h"
@ -279,6 +280,14 @@ public:
*/
void MediaFeatureValuesChanged(nsRestyleHint aRestyleHint,
nsChangeHint aChangeHint = nsChangeHint(0));
/**
* Calls MediaFeatureValuesChanged for this pres context and all descendant
* subdocuments that have a pres context. This should be used for media
* features that must be updated in all subdocuments e.g. display-mode.
*/
void MediaFeatureValuesChangedAllDocuments(nsRestyleHint aRestyleHint,
nsChangeHint aChangeHint = nsChangeHint(0));
void PostMediaFeatureValuesChangedEvent();
void HandleMediaFeatureValuesChangedEvent();
void FlushPendingMediaFeatureValuesChanged() {
@ -286,6 +295,13 @@ public:
MediaFeatureValuesChanged(nsRestyleHint(0));
}
/**
* Updates the size mode on all remote children and recursively notifies this
* document and all subdocuments (including remote children) that a media
* feature value has changed.
*/
void SizeModeChanged(nsSizeMode aSizeMode);
/**
* Access compatibility mode for this context. This is the same as
* our document's compatibility mode.

View File

@ -169,6 +169,7 @@ CSS_KEY(bottom-outside, bottom_outside)
CSS_KEY(break-all, break_all)
CSS_KEY(break-word, break_word)
CSS_KEY(brightness, brightness)
CSS_KEY(browser, browser)
CSS_KEY(bullets, bullets)
CSS_KEY(button, button)
CSS_KEY(buttonface, buttonface)
@ -280,6 +281,7 @@ CSS_KEY(forwards, forwards)
CSS_KEY(fraktur, fraktur)
CSS_KEY(from-image, from_image)
CSS_KEY(full-width, full_width)
CSS_KEY(fullscreen, fullscreen)
CSS_KEY(grab, grab)
CSS_KEY(grabbing, grabbing)
CSS_KEY(grad, grad)
@ -535,6 +537,7 @@ CSS_KEY(square, square)
CSS_KEY(stacked-fractions, stacked_fractions)
CSS_KEY(start, start)
CSS_KEY(static, static)
CSS_KEY(standalone, standalone)
CSS_KEY(status-bar, status_bar)
CSS_KEY(step-end, step_end)
CSS_KEY(step-start, step_start)
@ -706,6 +709,7 @@ CSS_KEY(menulist-text, menulist_text)
CSS_KEY(menulist-textfield, menulist_textfield)
CSS_KEY(meterbar, meterbar)
CSS_KEY(meterchunk, meterchunk)
CSS_KEY(minimal-ui, minimal_ui)
CSS_KEY(range, range)
CSS_KEY(range-thumb, range_thumb)
CSS_KEY(sans-serif, sans_serif)

View File

@ -17,6 +17,7 @@
#endif
#include "nsCSSRuleProcessor.h"
#include "nsDeviceContext.h"
#include "nsIBaseWindow.h"
#include "nsIDocument.h"
#include "nsContentUtils.h"
#include "mozilla/StyleSheetHandle.h"
@ -36,6 +37,14 @@ static const nsCSSProps::KTableEntry kScanKeywords[] = {
{ eCSSKeyword_UNKNOWN, -1 }
};
static const nsCSSProps::KTableEntry kDisplayModeKeywords[] = {
{ eCSSKeyword_browser, NS_STYLE_DISPLAY_MODE_BROWSER },
{ eCSSKeyword_minimal_ui, NS_STYLE_DISPLAY_MODE_MINIMAL_UI },
{ eCSSKeyword_standalone, NS_STYLE_DISPLAY_MODE_STANDALONE },
{ eCSSKeyword_fullscreen, NS_STYLE_DISPLAY_MODE_FULLSCREEN },
{ eCSSKeyword_UNKNOWN, -1 }
};
#ifdef XP_WIN
struct WindowsThemeName {
LookAndFeel::WindowsTheme id;
@ -304,6 +313,34 @@ GetScan(nsPresContext* aPresContext, const nsMediaFeature*,
return NS_OK;
}
static nsresult
GetDisplayMode(nsPresContext* aPresContext, const nsMediaFeature*,
nsCSSValue& aResult)
{
nsCOMPtr<nsISupports> container = aPresContext->GetRootPresContext()->
Document()->GetContainer();
nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
if (!baseWindow) {
aResult.SetIntValue(NS_STYLE_DISPLAY_MODE_BROWSER, eCSSUnit_Enumerated);
return NS_OK;
}
nsCOMPtr<nsIWidget> mainWidget;
baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
int32_t displayMode;
nsSizeMode mode = mainWidget ? mainWidget->SizeMode() : nsSizeMode_Normal;
switch (mode) {
case nsSizeMode_Fullscreen:
displayMode = NS_STYLE_DISPLAY_MODE_FULLSCREEN;
break;
default:
displayMode = NS_STYLE_DISPLAY_MODE_BROWSER;
break;
}
aResult.SetIntValue(displayMode, eCSSUnit_Enumerated);
return NS_OK;
}
static nsresult
GetGrid(nsPresContext* aPresContext, const nsMediaFeature*,
nsCSSValue& aResult)
@ -533,6 +570,14 @@ nsMediaFeatures::features[] = {
{ nullptr },
GetGrid
},
{
&nsGkAtoms::displayMode,
nsMediaFeature::eMinMaxNotAllowed,
nsMediaFeature::eEnumerated,
nsMediaFeature::eNoRequirements,
{ kDisplayModeKeywords },
GetDisplayMode
},
// Webkit extensions that we support for de-facto web compatibility
// -webkit-{min|max}-device-pixel-ratio (controlled with its own pref):

View File

@ -1205,6 +1205,12 @@ enum class FillMode : uint32_t;
#define NS_STYLE_SCAN_PROGRESSIVE 0
#define NS_STYLE_SCAN_INTERLACE 1
// display-mode
#define NS_STYLE_DISPLAY_MODE_BROWSER 0
#define NS_STYLE_DISPLAY_MODE_MINIMAL_UI 1
#define NS_STYLE_DISPLAY_MODE_STANDALONE 2
#define NS_STYLE_DISPLAY_MODE_FULLSCREEN 3
} // namespace mozilla
#endif /* nsStyleConsts_h___ */

View File

@ -16,6 +16,7 @@ support-files =
[test_bug1157097.html]
[test_bug1160724.xul]
[test_bug535806.xul]
[test_display_mode.html]
[test_hover.html]
skip-if = buildapp == 'mulet'
[test_moz_document_rules.html]

View File

@ -0,0 +1,94 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1104916
-->
<head>
<meta charset="utf-8">
<title>Test for Display Mode</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
<script type="application/javascript">
/** Test for Display Mode **/
SimpleTest.waitForExplicitFinish();
Components.utils.import("resource://gre/modules/Services.jsm");
function waitOneEvent(element, name) {
return new Promise(function(resolve, reject) {
element.addEventListener(name, function listener() {
element.removeEventListener(name, listener);
resolve();
});
});
}
add_task(function* () {
yield waitOneEvent(window, "load");
var iframe = document.getElementById("subdoc");
var subdoc = iframe.contentDocument;
var style = subdoc.getElementById("style");
var bodyComputedStyled = subdoc.defaultView.getComputedStyle(subdoc.body, "");
var win = Services.wm.getMostRecentWindow("navigator:browser");
function queryApplies(q) {
style.setAttribute("media", q);
return bodyComputedStyled.getPropertyValue("text-decoration") == "underline";
}
function shouldApply(q) {
ok(queryApplies(q), q + " should apply");
}
function shouldNotApply(q) {
ok(!queryApplies(q), q + " should not apply");
}
shouldApply("all and (display-mode: browser)");
shouldNotApply("all and (display-mode: fullscreen)");
shouldNotApply("all and (display-mode: standalone)");
shouldNotApply("all and (display-mode: minimal-ui)");
// Test entering the OS's fullscreen mode.
var fullScreenEntered = waitOneEvent(win, "sizemodechange");
synthesizeKey("VK_F11", {});
yield fullScreenEntered;
shouldApply("all and (display-mode: fullscreen)");
shouldNotApply("all and (display-mode: browser)");
var fullScreenExited = waitOneEvent(win, "sizemodechange");
synthesizeKey("VK_F11", {});
yield fullScreenExited;
shouldNotApply("all and (display-mode: fullscreen)");
shouldApply("all and (display-mode: browser)");
// Test entering fullscreen through document requestFullScreen.
fullScreenEntered = waitOneEvent(document, "mozfullscreenchange");
document.body.mozRequestFullScreen();
yield fullScreenEntered
ok(document.mozFullScreenElement, "window entered fullscreen");
shouldApply("all and (display-mode: fullscreen)");
shouldNotApply("all and (display-mode: browser)");
fullScreenExited = waitOneEvent(document, "mozfullscreenchange");
document.mozCancelFullScreen();
yield fullScreenExited;
ok(!document.mozFullScreenElement, "window exited fullscreen");
shouldNotApply("all and (display-mode: fullscreen)");
shouldApply("all and (display-mode: browser)");
});
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1104916">Mozilla Bug 1104916</a>
<iframe id="subdoc" src="http://mochi.test:8888/tests/layout/style/test/media_queries_iframe.html"></iframe>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -253,6 +253,14 @@ function run() {
expression_should_not_be_parseable("max-" + feature);
}
var mediatypes = ["browser", "minimal-ui", "standalone", "fullscreen"];
mediatypes.forEach(function(type) {
expression_should_be_parseable("display-mode: " + type);
});
expression_should_not_be_parseable("display-mode: invalid")
var content_div = document.getElementById("content");
content_div.style.font = "initial";
var em_size =

View File

@ -6159,6 +6159,8 @@ void nsWindow::OnWindowPosChanged(WINDOWPOS* wp)
pl.length = sizeof(pl);
::GetWindowPlacement(mWnd, &pl);
nsSizeMode previousSizeMode = mSizeMode;
// Windows has just changed the size mode of this window. The call to
// SizeModeChanged will trigger a call into SetSizeMode where we will
// set the min/max window state again or for nsSizeMode_Normal, call
@ -6203,7 +6205,7 @@ void nsWindow::OnWindowPosChanged(WINDOWPOS* wp)
}
#endif
if (mWidgetListener)
if (mWidgetListener && mSizeMode != previousSizeMode)
mWidgetListener->SizeModeChanged(mSizeMode);
// If window was restored, window activation was bypassed during the

View File

@ -365,6 +365,11 @@ nsWebShellWindow::SizeModeChanged(nsSizeMode sizeMode)
ourWindow->DispatchCustomEvent(NS_LITERAL_STRING("sizemodechange"));
}
nsIPresShell* presShell;
if ((presShell = GetPresShell())) {
presShell->GetPresContext()->SizeModeChanged(sizeMode);
}
// Note the current implementation of SetSizeMode just stores
// the new state; it doesn't actually resize. So here we store
// the state and pass the event on to the OS. The day is coming