Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
8 : #include "mozilla/dom/ScreenBinding.h"
9 : #include "nsContentUtils.h"
10 : #include "nsScreen.h"
11 : #include "nsIDocument.h"
12 : #include "nsIDocShell.h"
13 : #include "nsIDocument.h"
14 : #include "nsPresContext.h"
15 : #include "nsCOMPtr.h"
16 : #include "nsIDocShellTreeItem.h"
17 : #include "nsLayoutUtils.h"
18 : #include "nsJSUtils.h"
19 : #include "nsDeviceContext.h"
20 :
21 : using namespace mozilla;
22 : using namespace mozilla::dom;
23 :
24 : /* static */ already_AddRefed<nsScreen>
25 1 : nsScreen::Create(nsPIDOMWindowInner* aWindow)
26 : {
27 1 : MOZ_ASSERT(aWindow);
28 1 : MOZ_ASSERT(aWindow->IsInnerWindow());
29 :
30 1 : if (!aWindow->GetDocShell()) {
31 0 : return nullptr;
32 : }
33 :
34 2 : nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
35 1 : NS_ENSURE_TRUE(sgo, nullptr);
36 :
37 2 : RefPtr<nsScreen> screen = new nsScreen(aWindow);
38 1 : return screen.forget();
39 : }
40 :
41 1 : nsScreen::nsScreen(nsPIDOMWindowInner* aWindow)
42 : : DOMEventTargetHelper(aWindow)
43 2 : , mScreenOrientation(new ScreenOrientation(aWindow, this))
44 : {
45 1 : }
46 :
47 0 : nsScreen::~nsScreen()
48 : {
49 0 : }
50 :
51 :
52 : // QueryInterface implementation for nsScreen
53 13 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsScreen)
54 7 : NS_INTERFACE_MAP_ENTRY(nsIDOMScreen)
55 6 : NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
56 :
57 5 : NS_IMPL_ADDREF_INHERITED(nsScreen, DOMEventTargetHelper)
58 2 : NS_IMPL_RELEASE_INHERITED(nsScreen, DOMEventTargetHelper)
59 :
60 1 : NS_IMPL_CYCLE_COLLECTION_INHERITED(nsScreen,
61 : DOMEventTargetHelper,
62 : mScreenOrientation)
63 :
64 : int32_t
65 0 : nsScreen::GetPixelDepth(ErrorResult& aRv)
66 : {
67 : // Return 24 to prevent fingerprinting.
68 0 : if (ShouldResistFingerprinting()) {
69 0 : return 24;
70 : }
71 :
72 0 : nsDeviceContext* context = GetDeviceContext();
73 :
74 0 : if (!context) {
75 0 : aRv.Throw(NS_ERROR_FAILURE);
76 0 : return -1;
77 : }
78 :
79 : uint32_t depth;
80 0 : context->GetDepth(depth);
81 0 : return depth;
82 : }
83 :
84 : #define FORWARD_LONG_GETTER(_name) \
85 : NS_IMETHODIMP \
86 : nsScreen::Get ## _name(int32_t* aOut) \
87 : { \
88 : ErrorResult rv; \
89 : *aOut = Get ## _name(rv); \
90 : return rv.StealNSResult(); \
91 : }
92 :
93 1 : FORWARD_LONG_GETTER(AvailWidth)
94 1 : FORWARD_LONG_GETTER(AvailHeight)
95 :
96 0 : FORWARD_LONG_GETTER(Top)
97 0 : FORWARD_LONG_GETTER(AvailTop)
98 0 : FORWARD_LONG_GETTER(AvailLeft)
99 :
100 : nsPIDOMWindowOuter*
101 10 : nsScreen::GetOuter() const
102 : {
103 10 : if (nsPIDOMWindowInner* inner = GetOwner()) {
104 10 : return inner->GetOuterWindow();
105 : }
106 :
107 0 : return nullptr;
108 : }
109 :
110 : nsDeviceContext*
111 10 : nsScreen::GetDeviceContext()
112 : {
113 10 : return nsLayoutUtils::GetDeviceContextForScreenInfo(GetOuter());
114 : }
115 :
116 : nsresult
117 6 : nsScreen::GetRect(nsRect& aRect)
118 : {
119 : // Return window inner rect to prevent fingerprinting.
120 6 : if (ShouldResistFingerprinting()) {
121 0 : return GetWindowInnerRect(aRect);
122 : }
123 :
124 6 : nsDeviceContext *context = GetDeviceContext();
125 :
126 6 : if (!context) {
127 0 : return NS_ERROR_FAILURE;
128 : }
129 :
130 6 : context->GetRect(aRect);
131 : LayoutDevicePoint screenTopLeftDev =
132 12 : LayoutDevicePixel::FromAppUnits(aRect.TopLeft(),
133 12 : context->AppUnitsPerDevPixel());
134 : DesktopPoint screenTopLeftDesk =
135 6 : screenTopLeftDev / context->GetDesktopToDeviceScale();
136 :
137 6 : aRect.x = NSToIntRound(screenTopLeftDesk.x);
138 6 : aRect.y = NSToIntRound(screenTopLeftDesk.y);
139 :
140 6 : aRect.height = nsPresContext::AppUnitsToIntCSSPixels(aRect.height);
141 6 : aRect.width = nsPresContext::AppUnitsToIntCSSPixels(aRect.width);
142 :
143 6 : return NS_OK;
144 : }
145 :
146 : nsresult
147 4 : nsScreen::GetAvailRect(nsRect& aRect)
148 : {
149 : // Return window inner rect to prevent fingerprinting.
150 4 : if (ShouldResistFingerprinting()) {
151 0 : return GetWindowInnerRect(aRect);
152 : }
153 :
154 4 : nsDeviceContext *context = GetDeviceContext();
155 :
156 4 : if (!context) {
157 0 : return NS_ERROR_FAILURE;
158 : }
159 :
160 8 : nsRect r;
161 4 : context->GetRect(r);
162 : LayoutDevicePoint screenTopLeftDev =
163 8 : LayoutDevicePixel::FromAppUnits(r.TopLeft(),
164 8 : context->AppUnitsPerDevPixel());
165 : DesktopPoint screenTopLeftDesk =
166 4 : screenTopLeftDev / context->GetDesktopToDeviceScale();
167 :
168 4 : context->GetClientRect(aRect);
169 :
170 8 : aRect.x = NSToIntRound(screenTopLeftDesk.x) +
171 4 : nsPresContext::AppUnitsToIntCSSPixels(aRect.x - r.x);
172 8 : aRect.y = NSToIntRound(screenTopLeftDesk.y) +
173 4 : nsPresContext::AppUnitsToIntCSSPixels(aRect.y - r.y);
174 :
175 4 : aRect.height = nsPresContext::AppUnitsToIntCSSPixels(aRect.height);
176 4 : aRect.width = nsPresContext::AppUnitsToIntCSSPixels(aRect.width);
177 :
178 4 : return NS_OK;
179 : }
180 :
181 : mozilla::dom::ScreenOrientation*
182 0 : nsScreen::Orientation() const
183 : {
184 0 : return mScreenOrientation;
185 : }
186 :
187 : void
188 0 : nsScreen::GetMozOrientation(nsString& aOrientation,
189 : CallerType aCallerType) const
190 : {
191 0 : switch (mScreenOrientation->DeviceType(aCallerType)) {
192 : case OrientationType::Portrait_primary:
193 0 : aOrientation.AssignLiteral("portrait-primary");
194 0 : break;
195 : case OrientationType::Portrait_secondary:
196 0 : aOrientation.AssignLiteral("portrait-secondary");
197 0 : break;
198 : case OrientationType::Landscape_primary:
199 0 : aOrientation.AssignLiteral("landscape-primary");
200 0 : break;
201 : case OrientationType::Landscape_secondary:
202 0 : aOrientation.AssignLiteral("landscape-secondary");
203 0 : break;
204 : default:
205 0 : MOZ_CRASH("Unacceptable screen orientation type.");
206 : }
207 0 : }
208 :
209 : static void
210 0 : UpdateDocShellOrientationLock(nsPIDOMWindowInner* aWindow,
211 : ScreenOrientationInternal aOrientation)
212 : {
213 0 : if (!aWindow) {
214 0 : return;
215 : }
216 :
217 0 : nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
218 0 : if (!docShell) {
219 0 : return;
220 : }
221 :
222 0 : nsCOMPtr<nsIDocShellTreeItem> root;
223 0 : docShell->GetSameTypeRootTreeItem(getter_AddRefs(root));
224 0 : nsCOMPtr<nsIDocShell> rootShell(do_QueryInterface(root));
225 0 : if (!rootShell) {
226 0 : return;
227 : }
228 :
229 0 : rootShell->SetOrientationLock(aOrientation);
230 : }
231 :
232 : bool
233 0 : nsScreen::MozLockOrientation(const nsAString& aOrientation, ErrorResult& aRv)
234 : {
235 0 : nsString orientation(aOrientation);
236 0 : Sequence<nsString> orientations;
237 0 : if (!orientations.AppendElement(orientation, fallible)) {
238 0 : aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
239 0 : return false;
240 : }
241 0 : return MozLockOrientation(orientations, aRv);
242 : }
243 :
244 : bool
245 0 : nsScreen::MozLockOrientation(const Sequence<nsString>& aOrientations,
246 : ErrorResult& aRv)
247 : {
248 0 : if (ShouldResistFingerprinting()) {
249 0 : return false;
250 : }
251 0 : ScreenOrientationInternal orientation = eScreenOrientation_None;
252 :
253 0 : for (uint32_t i = 0; i < aOrientations.Length(); ++i) {
254 0 : const nsString& item = aOrientations[i];
255 :
256 0 : if (item.EqualsLiteral("portrait")) {
257 0 : orientation |= eScreenOrientation_PortraitPrimary |
258 : eScreenOrientation_PortraitSecondary;
259 0 : } else if (item.EqualsLiteral("portrait-primary")) {
260 0 : orientation |= eScreenOrientation_PortraitPrimary;
261 0 : } else if (item.EqualsLiteral("portrait-secondary")) {
262 0 : orientation |= eScreenOrientation_PortraitSecondary;
263 0 : } else if (item.EqualsLiteral("landscape")) {
264 0 : orientation |= eScreenOrientation_LandscapePrimary |
265 : eScreenOrientation_LandscapeSecondary;
266 0 : } else if (item.EqualsLiteral("landscape-primary")) {
267 0 : orientation |= eScreenOrientation_LandscapePrimary;
268 0 : } else if (item.EqualsLiteral("landscape-secondary")) {
269 0 : orientation |= eScreenOrientation_LandscapeSecondary;
270 0 : } else if (item.EqualsLiteral("default")) {
271 0 : orientation |= eScreenOrientation_Default;
272 : } else {
273 : // If we don't recognize the token, we should just return 'false'
274 : // without throwing.
275 0 : return false;
276 : }
277 : }
278 :
279 0 : switch (mScreenOrientation->GetLockOrientationPermission(false)) {
280 : case ScreenOrientation::LOCK_DENIED:
281 0 : return false;
282 : case ScreenOrientation::LOCK_ALLOWED:
283 0 : UpdateDocShellOrientationLock(GetOwner(), orientation);
284 0 : return mScreenOrientation->LockDeviceOrientation(orientation, false, aRv);
285 : case ScreenOrientation::FULLSCREEN_LOCK_ALLOWED:
286 0 : UpdateDocShellOrientationLock(GetOwner(), orientation);
287 0 : return mScreenOrientation->LockDeviceOrientation(orientation, true, aRv);
288 : }
289 :
290 : // This is only for compilers that don't understand that the previous switch
291 : // will always return.
292 0 : MOZ_CRASH("unexpected lock orientation permission value");
293 : }
294 :
295 : void
296 0 : nsScreen::MozUnlockOrientation()
297 : {
298 0 : if (ShouldResistFingerprinting()) {
299 0 : return;
300 : }
301 0 : UpdateDocShellOrientationLock(GetOwner(), eScreenOrientation_None);
302 0 : mScreenOrientation->UnlockDeviceOrientation();
303 : }
304 :
305 : bool
306 6 : nsScreen::IsDeviceSizePageSize()
307 : {
308 6 : if (nsPIDOMWindowInner* owner = GetOwner()) {
309 6 : nsIDocShell* docShell = owner->GetDocShell();
310 6 : if (docShell) {
311 6 : return docShell->GetDeviceSizeIsPageSize();
312 : }
313 : }
314 0 : return false;
315 : }
316 :
317 : /* virtual */
318 : JSObject*
319 1 : nsScreen::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
320 : {
321 1 : return ScreenBinding::Wrap(aCx, this, aGivenProto);
322 : }
323 :
324 : nsresult
325 0 : nsScreen::GetWindowInnerRect(nsRect& aRect)
326 : {
327 0 : aRect.x = 0;
328 0 : aRect.y = 0;
329 0 : nsCOMPtr<nsPIDOMWindowInner> win = GetOwner();
330 0 : if (!win) {
331 0 : return NS_ERROR_FAILURE;
332 : }
333 0 : nsresult rv = win->GetInnerWidth(&aRect.width);
334 0 : NS_ENSURE_SUCCESS(rv, rv);
335 0 : return win->GetInnerHeight(&aRect.height);
336 : }
337 :
338 10 : bool nsScreen::ShouldResistFingerprinting() const
339 : {
340 10 : bool resist = false;
341 20 : nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
342 10 : if (owner) {
343 10 : resist = nsContentUtils::ShouldResistFingerprinting(owner->GetDocShell());
344 : }
345 20 : return resist;
346 : }
|