Skip to content

Commit e6e4456

Browse files
committed
Bug 1974167 - Add API to set screen orientation override without enabling RDM. r=dom-core,smaug
Differential Revision: https://phabricator.services.mozilla.com/D262787
1 parent 2f2e1a1 commit e6e4456

File tree

7 files changed

+281
-29
lines changed

7 files changed

+281
-29
lines changed

docshell/base/BrowsingContext.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
#include "nsContentUtils.h"
8686
#include "nsQueryObject.h"
8787
#include "nsSandboxFlags.h"
88+
#include "nsScreen.h"
8889
#include "nsScriptError.h"
8990
#include "nsThreadUtils.h"
9091
#include "xpcprivate.h"
@@ -2972,9 +2973,53 @@ void BrowsingContext::DidSet(FieldIndex<IDX_InRDMPane>, bool aOldValue) {
29722973
if (GetInRDMPane() == aOldValue) {
29732974
return;
29742975
}
2976+
2977+
// Reset screen orientation override when disabling RDM.
2978+
if (!GetInRDMPane()) {
2979+
ResetOrientationOverride();
2980+
}
2981+
29752982
PresContextAffectingFieldChanged();
29762983
}
29772984

2985+
void BrowsingContext::DidSet(FieldIndex<IDX_HasOrientationOverride>,
2986+
bool aOldValue) {
2987+
bool hasOrientationOverride = GetHasOrientationOverride();
2988+
OrientationType type = GetCurrentOrientationType();
2989+
float angle = GetCurrentOrientationAngle();
2990+
2991+
PreOrderWalk([&](BrowsingContext* aBrowsingContext) {
2992+
if (RefPtr<WindowContext> windowContext =
2993+
aBrowsingContext->GetCurrentWindowContext()) {
2994+
if (nsCOMPtr<nsPIDOMWindowInner> window =
2995+
windowContext->GetInnerWindow()) {
2996+
ScreenOrientation* orientation =
2997+
nsGlobalWindowInner::Cast(window)->Screen()->Orientation();
2998+
2999+
float screenOrientationAngle =
3000+
orientation->DeviceAngle(CallerType::System);
3001+
OrientationType screenOrientationType =
3002+
orientation->DeviceType(CallerType::System);
3003+
3004+
bool overrideIsDifferentThanDevice =
3005+
screenOrientationType != type || screenOrientationAngle != angle;
3006+
3007+
// Reset orientation override.
3008+
if (!hasOrientationOverride && aOldValue) {
3009+
Unused << aBrowsingContext->SetCurrentOrientation(
3010+
screenOrientationType, screenOrientationAngle);
3011+
} else if (!aBrowsingContext->IsTop()) {
3012+
// Sync orientation override in the existing frames.
3013+
Unused << aBrowsingContext->SetCurrentOrientation(type, angle);
3014+
}
3015+
3016+
orientation->MaybeDispatchEventsForOverride(
3017+
aBrowsingContext, aOldValue, overrideIsDifferentThanDevice);
3018+
}
3019+
}
3020+
});
3021+
}
3022+
29783023
void BrowsingContext::DidSet(FieldIndex<IDX_ForceDesktopViewport>,
29793024
bool aOldValue) {
29803025
MOZ_ASSERT(IsTop(), "Should only set in the top-level browsing context");

docshell/base/BrowsingContext.h

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ struct EmbedderColorSchemes {
208208
FIELD(CurrentOrientationAngle, float) \
209209
FIELD(CurrentOrientationType, mozilla::dom::OrientationType) \
210210
FIELD(OrientationLock, mozilla::hal::ScreenOrientation) \
211+
FIELD(HasOrientationOverride, bool) \
211212
FIELD(UserAgentOverride, nsString) \
212213
FIELD(TouchEventsOverrideInternal, mozilla::dom::TouchEventsOverride) \
213214
FIELD(EmbedderElementType, Maybe<nsString>) \
@@ -691,13 +692,37 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
691692
return txn.Commit(this);
692693
}
693694

694-
void SetRDMPaneOrientation(OrientationType aType, float aAngle,
695-
ErrorResult& aRv) {
696-
if (InRDMPane()) {
697-
if (NS_FAILED(SetCurrentOrientation(aType, aAngle))) {
698-
aRv.ThrowInvalidStateError("Browsing context is discarded");
699-
}
695+
bool HasOrientationOverride() const {
696+
return Top()->GetHasOrientationOverride();
697+
}
698+
699+
[[nodiscard]] nsresult SetOrientationOverride(OrientationType aType,
700+
float aAngle) {
701+
if (GetHasOrientationOverride() && GetCurrentOrientationType() == aType &&
702+
GetCurrentOrientationAngle() == aAngle) {
703+
return NS_OK;
700704
}
705+
706+
Transaction txn;
707+
txn.SetCurrentOrientationType(aType);
708+
txn.SetCurrentOrientationAngle(aAngle);
709+
txn.SetHasOrientationOverride(true);
710+
return txn.Commit(this);
711+
}
712+
713+
void SetOrientationOverride(OrientationType aType, float aAngle,
714+
ErrorResult& aRv) {
715+
MOZ_ASSERT(IsTop());
716+
717+
if (NS_FAILED(SetOrientationOverride(aType, aAngle))) {
718+
aRv.ThrowInvalidStateError("Browsing context is discarded");
719+
}
720+
}
721+
722+
void ResetOrientationOverride() {
723+
MOZ_ASSERT(IsTop());
724+
725+
Unused << SetHasOrientationOverride(false);
701726
}
702727

703728
void SetRDMPaneMaxTouchPoints(uint8_t aMaxTouchPoints, ErrorResult& aRv) {
@@ -1171,6 +1196,7 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
11711196
}
11721197

11731198
void DidSet(FieldIndex<IDX_InRDMPane>, bool aOldValue);
1199+
void DidSet(FieldIndex<IDX_HasOrientationOverride>, bool aOldValue);
11741200
MOZ_CAN_RUN_SCRIPT_BOUNDARY void DidSet(FieldIndex<IDX_ForceDesktopViewport>,
11751201
bool aOldValue);
11761202

dom/base/ScreenOrientation.cpp

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,14 @@ ScreenOrientation::ScreenOrientation(nsPIDOMWindowInner* aWindow,
7575

7676
Document* doc = GetResponsibleDocument();
7777
BrowsingContext* bc = doc ? doc->GetBrowsingContext() : nullptr;
78-
if (bc && !bc->IsDiscarded() && !bc->InRDMPane()) {
78+
if (bc && !bc->IsDiscarded() && !bc->HasOrientationOverride()) {
7979
MOZ_ALWAYS_SUCCEEDS(bc->SetCurrentOrientation(mType, mAngle));
80+
} else if (bc && !bc->IsTop() && bc->HasOrientationOverride()) {
81+
// Resync the override for newly created iframes.
82+
BrowsingContext* topBC = bc->Top();
83+
MOZ_ALWAYS_SUCCEEDS(
84+
bc->SetOrientationOverride(topBC->GetCurrentOrientationType(),
85+
topBC->GetCurrentOrientationAngle()));
8086
}
8187
}
8288

@@ -820,29 +826,52 @@ void ScreenOrientation::MaybeChanged() {
820826
rv = bc->SetCurrentOrientation(mType, mAngle);
821827
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "SetCurrentOrientation failed");
822828

823-
// change event has to be dispatched by descendantDocs.
824-
// Looking for top level browsing context that has screen in process.
825-
// If parent document has screen, we don't dispatch it at this time.
826-
// change event will be dispatched by parent's
827-
// ScreenOrientation::MaybeChanged.
828-
BrowsingContext* rootBc = bc;
829-
bool dispatchChangeEvent = true;
830-
while (rootBc->GetParent()) {
831-
rootBc = rootBc->GetParent();
832-
if (Document* doc = rootBc->GetExtantDocument()) {
833-
if (auto* win = nsGlobalWindowInner::Cast(doc->GetInnerWindow())) {
834-
if (win->HasScreen()) {
835-
// Parent of browsing context has screen object. Child shouldn't
836-
// dispatch change event.
837-
dispatchChangeEvent = false;
838-
break;
839-
}
829+
MaybeDispatchChangeEvent(bc);
830+
}
831+
}
832+
833+
void ScreenOrientation::MaybeDispatchChangeEvent(
834+
BrowsingContext* aBrowsingContext) {
835+
// change event has to be dispatched by descendantDocs.
836+
// Looking for top level browsing context that has screen in process.
837+
// If parent document has screen, we don't dispatch it at this time.
838+
// change event will be dispatched by parent's
839+
// ScreenOrientation::MaybeChanged.
840+
BrowsingContext* rootBc = aBrowsingContext;
841+
bool dispatchChangeEvent = true;
842+
while (rootBc->GetParent()) {
843+
rootBc = rootBc->GetParent();
844+
if (Document* doc = rootBc->GetExtantDocument()) {
845+
if (auto* win = nsGlobalWindowInner::Cast(doc->GetInnerWindow())) {
846+
if (win->HasScreen()) {
847+
// Parent of browsing context has screen object. Child shouldn't
848+
// dispatch change event.
849+
dispatchChangeEvent = false;
850+
break;
840851
}
841852
}
842853
}
843-
if (dispatchChangeEvent) {
844-
DispatchChangeEventToChildren(rootBc);
845-
}
854+
}
855+
if (dispatchChangeEvent) {
856+
DispatchChangeEventToChildren(rootBc);
857+
}
858+
}
859+
860+
void ScreenOrientation::MaybeDispatchEventsForOverride(
861+
BrowsingContext* aBrowsingContext, bool aOldHasOrientationOverride,
862+
bool aOverrideIsDifferentThanDevice) {
863+
Document* doc = aBrowsingContext->GetExtantDocument();
864+
nsCOMPtr<nsPIDOMWindowOuter> outerWindow = doc->GetWindow();
865+
866+
// Send the event if the orientation was already overriden or different
867+
// from device metrics or the override was reset and it is different from
868+
// device metrics.
869+
if ((aBrowsingContext->HasOrientationOverride() &&
870+
(aOldHasOrientationOverride || aOverrideIsDifferentThanDevice)) ||
871+
(!aBrowsingContext->HasOrientationOverride() &&
872+
aOldHasOrientationOverride && aOverrideIsDifferentThanDevice)) {
873+
outerWindow->DispatchCustomEvent(u"orientationchange"_ns);
874+
MaybeDispatchChangeEvent(aBrowsingContext);
846875
}
847876
}
848877

dom/base/ScreenOrientation.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ class ScreenOrientation final : public DOMEventTargetHelper {
3333
// Called when the orientation may have changed.
3434
void MaybeChanged();
3535

36+
// Called when we might need to dispatch orientation change event.
37+
void MaybeDispatchChangeEvent(BrowsingContext* aBrowsingContext);
38+
39+
// Called when we might need to dispatch orientation change events
40+
// in case of orientation override being set, updated or removed.
41+
void MaybeDispatchEventsForOverride(BrowsingContext* aBrowsingContext,
42+
bool aOldHasOrientationOverride,
43+
bool aOverrideIsDifferentThanDevice);
44+
3645
ScreenOrientation(nsPIDOMWindowInner* aWindow, nsScreen* aScreen);
3746

3847
already_AddRefed<Promise> Lock(OrientationLockType aOrientation,

dom/base/test/browser.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ support-files = [
152152
"file_browser_refresh_iframe.sjs",
153153
]
154154

155+
["browser_screen_orientation_override.js"]
156+
155157
["browser_set_focus_after_reuse_bcg.js"]
156158

157159
["browser_state_notifications.js"]
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/* Any copyright is dedicated to the Public Domain.
2+
http://creativecommons.org/publicdomain/zero/1.0/ */
3+
4+
"use strict";
5+
6+
const PAGE_URL =
7+
"https://example.com/document-builder.sjs?html=<h1>Test orientation simulation</h1>";
8+
9+
add_task(async function test_set_orientation_override() {
10+
const tab = BrowserTestUtils.addTab(gBrowser, PAGE_URL);
11+
const browser = gBrowser.getBrowserForTab(tab);
12+
13+
await BrowserTestUtils.browserLoaded(browser);
14+
15+
info("Get default orientation values");
16+
const defaultOrientationAngle = await getOrientationAngle(browser);
17+
const defaultOrientationType = await getOrientationType(browser);
18+
19+
info("Set orientation override");
20+
const orientationAngleOverride1 = 180;
21+
const orientationTypeOverride1 = "portrait-primary";
22+
browser.browsingContext.setOrientationOverride(
23+
orientationTypeOverride1,
24+
orientationAngleOverride1
25+
);
26+
27+
await assertOrientationOverride(
28+
browser,
29+
orientationAngleOverride1,
30+
orientationTypeOverride1
31+
);
32+
33+
info("Set another orientation override");
34+
const orientationAngleOverride2 = 90;
35+
const orientationTypeOverride2 = "landscape-secondary";
36+
browser.browsingContext.setOrientationOverride(
37+
orientationTypeOverride2,
38+
orientationAngleOverride2
39+
);
40+
41+
await assertOrientationOverride(
42+
browser,
43+
orientationAngleOverride2,
44+
orientationTypeOverride2
45+
);
46+
47+
info("Reset orientation override");
48+
browser.browsingContext.resetOrientationOverride();
49+
50+
await assertOrientationOverride(
51+
browser,
52+
defaultOrientationAngle,
53+
defaultOrientationType
54+
);
55+
56+
BrowserTestUtils.removeTab(tab);
57+
});
58+
59+
add_task(async function test_set_orientation_override_in_different_contexts() {
60+
const tab1 = BrowserTestUtils.addTab(gBrowser, PAGE_URL);
61+
const browser1 = gBrowser.getBrowserForTab(tab1);
62+
63+
await BrowserTestUtils.browserLoaded(browser1);
64+
65+
const tab2 = BrowserTestUtils.addTab(gBrowser, PAGE_URL);
66+
const browser2 = gBrowser.getBrowserForTab(tab2);
67+
68+
await BrowserTestUtils.browserLoaded(browser2);
69+
70+
info("Get default orientation values");
71+
const defaultOrientationAngle = await getOrientationAngle(browser1);
72+
const defaultOrientationType = await getOrientationType(browser1);
73+
74+
info("Set orientation override to the first tab");
75+
const orientationAngleOverride = 180;
76+
const orientationTypeOverride = "portrait-primary";
77+
browser1.browsingContext.setOrientationOverride(
78+
orientationTypeOverride,
79+
orientationAngleOverride
80+
);
81+
82+
await assertOrientationOverride(
83+
browser1,
84+
orientationAngleOverride,
85+
orientationTypeOverride
86+
);
87+
88+
info("Make sure that in the second tab orientation is not overridden");
89+
await assertOrientationOverride(
90+
browser2,
91+
defaultOrientationAngle,
92+
defaultOrientationType
93+
);
94+
95+
info("Reset orientation override in the first tab");
96+
browser1.browsingContext.resetOrientationOverride();
97+
await assertOrientationOverride(
98+
browser1,
99+
defaultOrientationAngle,
100+
defaultOrientationType
101+
);
102+
103+
BrowserTestUtils.removeTab(tab1);
104+
BrowserTestUtils.removeTab(tab2);
105+
});
106+
107+
async function assertOrientationOverride(
108+
browser,
109+
expectedAngleValue,
110+
expectedTypeValue
111+
) {
112+
is(
113+
await getOrientationAngle(browser),
114+
expectedAngleValue,
115+
"screen.orientation.angle has expected value"
116+
);
117+
is(
118+
await getOrientationType(browser),
119+
expectedTypeValue,
120+
"screen.orientation.type has expected value"
121+
);
122+
}
123+
124+
async function getOrientationAngle(browser) {
125+
return await SpecialPowers.spawn(
126+
browser,
127+
[],
128+
() => content.screen.orientation.angle
129+
);
130+
}
131+
132+
async function getOrientationType(browser) {
133+
return await SpecialPowers.spawn(
134+
browser,
135+
[],
136+
() => content.screen.orientation.type
137+
);
138+
}

dom/chrome-webidl/BrowsingContext.webidl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,11 @@ interface BrowsingContext {
198198
[SetterThrows] attribute boolean useGlobalHistory;
199199

200200
// Extension to give chrome JS the ability to set the window screen
201-
// orientation while in RDM.
202-
[Throws] undefined setRDMPaneOrientation(OrientationType type, float rotationAngle);
201+
// orientation override with WebDriver BiDi and DevTools.
202+
[Throws] undefined setOrientationOverride(OrientationType type, float rotationAngle);
203+
// Extension to give chrome JS the ability to reset the window screen
204+
// orientation override with WebDriver BiDi and DevTools.
205+
undefined resetOrientationOverride();
203206

204207
// Extension to give chrome JS the ability to set a maxTouchPoints override
205208
// while in RDM.

0 commit comments

Comments
 (0)