8000 Bug 1715482 - Geolocation should gracefully handle Documents that are… · jamienicol/gecko@2c3d787 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2c3d787

Browse files
committed
Bug 1715482 - Geolocation should gracefully handle Documents that are not fully active r=saschanaz
Spec change w3c/geolocation#97 Differential Revision: https://phabricator.services.mozilla.com/D117273
1 parent 0541a62 commit 2c3d787

File tree

5 files changed

+174
-22
lines changed

5 files changed

+174
-22
lines changed

dom/geolocation/Geolocation.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,16 @@ Geolocation::NotifyError(uint16_t aErrorCode) {
922922
return NS_OK;
923923
}
924924

925+
bool Geolocation::IsFullyActiveOrChrome() {
926+
// For regular content window, only allow this proceed if the window is "fully
927+
// active".
928+
if (nsPIDOMWindowInner* window = this->GetParentObject()) {
929+
return window->IsFullyActive();
930+
}
931+
// Calls coming from chrome code don't have window, so we can proceed.
932+
return true;
933+
}
934+
925935
bool Geolocation::IsAlreadyCleared(nsGeolocationRequest* aRequest) {
926936
for (uint32_t i = 0, length = mClearedWatchIDs.Length(); i < length; ++i) {
927937
if (mClearedWatchIDs[i] == aRequest->WatchId()) {
@@ -993,6 +1003,14 @@ nsresult Geolocation::GetCurrentPosition(GeoPositionCallback callback,
9931003
GeoPositionErrorCallback errorCallback,
9941004
UniquePtr<PositionOptions>&& options,
9951005
CallerType aCallerType) {
1006+
if (!IsFullyActiveOrChrome()) {
1007+
RefPtr<GeolocationPositionError> positionError =
1008+
new GeolocationPositionError(
1009+
this, GeolocationPositionError_Binding::POSITION_UNAVAILABLE);
1010+
positionError->NotifyCallback(errorCallback);
1011+
return NS_OK;
1012+
}
1013+
9961014
if (mPendingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
9971015
return NS_ERROR_NOT_AVAILABLE;
9981016
}
@@ -1059,6 +1077,14 @@ int32_t Geolocation::WatchPosition(GeoPositionCallback aCallback,
10591077
GeoPositionErrorCallback aErrorCallback,
10601078
UniquePtr<PositionOptions>&& aOptions,
10611079
CallerType aCallerType, ErrorResult& aRv) {
1080+
if (!IsFullyActiveOrChrome()) {
1081+
RefPtr<GeolocationPositionError> positionError =
1082+
new GeolocationPositionError(
1083+
this, GeolocationPositionError_Binding::POSITION_UNAVAILABLE);
1084+
positionError->NotifyCallback(aErrorCallback);
1085+
return 0;
1086+
}
1087+
10621088
if (mWatchingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
10631089
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
10641090
return 0;

dom/geolocation/Geolocation.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ class Geolocation final : public nsIGeolocationUpdate, public nsWrapperCache {
138138
PositionErrorCallback* aErrorCallback,
139139
const PositionOptions& aOptions, CallerType aCallerType,
140140
ErrorResult& aRv);
141+
142+
MOZ_CAN_RUN_SCRIPT
141143
void GetCurrentPosition(PositionCallback& aCallback,
142144
PositionErrorCallback* aErrorCallback,
143145
const PositionOptions& aOptions,
@@ -185,10 +187,12 @@ class Geolocation final : public nsIGeolocationUpdate, public nsWrapperCache {
185187
private:
186188
~Geolocation();
187189

190+
MOZ_CAN_RUN_SCRIPT
188191
nsresult GetCurrentPosition(GeoPositionCallback aCallback,
189192
GeoPositionErrorCallback aErrorCallback,
190193
UniquePtr<PositionOptions>&& aOptions,
191194
CallerType aCallerType);
195+
192196
MOZ_CAN_RUN_SCRIPT
193197
int32_t WatchPosition(GeoPositionCallback aCallback,
194198
GeoPositionErrorCallback aErrorCallback,
@@ -204,6 +208,10 @@ class Geolocation final : public nsIGeolocationUpdate, public nsWrapperCache {
204208
// within a context that is not secure.
205209
bool ShouldBlockInsecureRequests() const;
206210

211+
// Checks if the request is in a content window that is fully active, or the
212+
// request is coming from a chrome window.
213+
bool IsFullyActiveOrChrome();
214+
207215
// Two callback arrays. The first |mPendingCallbacks| holds objects for only
208216
// one callback and then they are released/removed from the array. The second
209217
// |mWatchingCallbacks| holds objects until the object is explictly removed or

dom/tests/mochitest/chrome/test_geolocation.xhtml

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,51 @@
44
<!--
55
Test for Geolocation in chrome
66
-->
7-
<window id="sample-window" width="400" height="400"
8-
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
9-
<script type="application/javascript"
10-
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
7+
<window
8+
id="sample-window"
9+
width="400"
10+
height="400"
11+
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
12+
>
13+
<script
14+
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"
15+
></script>
1116

12-
<script>
13-
SimpleTest.waitForExplicitFinish();
17+
<script>
18+
SimpleTest.waitForExplicitFinish();
19+
async function test() {
20+
/** @type {Geolocation} */
21+
const geolocation = Cc["@mozilla.org/geolocation;1"].getService(
22+
Ci.nsISupports
23+
);
24+
try {
25+
// Watch position
26+
let watchId;
27+
let position = await new Promise((resolve, reject) => {
28+
watchId = geolocation.watchPosition(resolve, reject, { timeout: 0 });
29+
});
30+
ok(position, "watchPosition() callable from chrome");
31+
geolocation.clearWatch(watchId);
1432

15-
var geolocation = Cc["@mozilla.org/geolocation;1"].getService(Ci.nsISupports);
16-
geolocation.getCurrentPosition(done, error);
17-
18-
function error(error)
19-
{
20-
ok(0, "error occured trying to get geolocation from chrome");
21-
SimpleTest.finish();
22-
}
23-
function done(position)
24-
{
25-
ok(position, "geolocation was found from chrome");
26-
SimpleTest.finish();
27-
}
28-
</script>
29-
30-
<body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/>
33+
// Get position
34+
position = await new Promise((resolve, reject) =>
35+
geolocation.getCurrentPosition(resolve, reject)
36+
);
37+
ok(position, "getCurrentPosition() callable from chrome");
38+
} catch (err) {
39+
ok(
40+
false,
41+
"error occurred trying to get geolocation from chrome: " + err.message
42+
);
43+
} finally {
44+
SimpleTest.finish();
45+
}
46+
}
47+
</script>
3148

49+
<body
50+
xmlns="http://www.w3.org/1999/xhtml"
51+
style="height: 300px; overflow: auto;"
52+
onload="test()"
53+
/>
3254
</window>

dom/tests/mochitest/geolocation/mochitest.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ skip-if = xorigin # Hangs
5757
[test_featurePolicy.html]
5858
support-files = file_featurePolicy.html
5959
fail-if = xorigin
60+
[test_not_fully_active.html]
61+
skip-if = xorigin # Hangs
62+
support-files = popup.html
6063

6164
# This test REQUIRES to run on HTTP (_NOT_ HTTPS).
6265
[test_geoWatchPositionBlockedInInsecureContext.html]
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>
5+
Test for when geolocation is used on non fully active documents
6+
</title>
7+
<script src="/tests/SimpleTest/SimpleTest.js"></script>
8+
<script src="geolocation_common.js"></script>
9+
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
10+
</head>
11+
<body>
12+
<script>
13+
SimpleTest.waitForExplicitFinish();
14+
15+
async function runTest() {
16+
// Create the iframe, wait for it to load...
17+
const iframe = document.createElement("iframe");
18+
19+
// We rely on this popup.html to acquire prompt privileges.
20+
iframe.src = "popup.html";
21+
document.body.appendChild(iframe);
22+
iframe.contentWindow.opener = window;
23+
await new Promise(r => window.addEventListener("message", r, { once: true }));
24+
25+
// Steal geolocation.
26+
const geo = iframe.contentWindow.navigator.geolocation;
27+
28+
// No longer fully active.
29+
iframe.remove();
30+
31+
// Try to watch a position while not fully active...
32+
const watchError = await new Promise((resolve, reject) => {
33+
const result = geo.watchPosition(
34+
reject, // We don't want a position
35+
resolve // We want an error!
36+
);
37+
is(result, 0, "watchPosition returns 0 on non-fully-active document");
38+
});
39+
is(
40+
watchError.code,
41+
GeolocationPositionError.POSITION_UNAVAILABLE,
42+
"watchPosition returns an error on non-fully-active document"
43+
);
44+
45+
// Now try to get current position while not fully active...
46+
const positionError = await new Promise((resolve, reject) => {
47+
const result = geo.getCurrentPosition(
48+
reject, // We don't want a position
49+
resolve // We want an error!
50+
);
51+
});
52+
is(
53+
positionError.code,
54+
GeolocationPositionError.POSITION_UNAVAILABLE,
55+
"getCurrentPosition returns an error on non-fully-active document"
56+
);
57+
58+
// Re-attach, and go back to fully active.
59+
document.body.appendChild(iframe);
60+
iframe.contentWindow.opener = window;
61+
await new Promise(r => window.addEventListener("message", r, { once: true }));
62+
63+
// And we are back to fully active.
64+
let watchId;
65+
let position = await new Promise((resolve, reject) => {
66+
watchId = iframe.contentWindow.navigator.geolocation.watchPosition(
67+
resolve,
68+
reject
69+
);
70+
});
71+
ok(watchId > 0, "Expected anything greater than 0");
72+
ok(position, "Expected a position");
73+
74+
// Finally, let's get the position from the reattached document.
75+
position = await new Promise((resolve, reject) => {
76+
iframe.contentWindow.navigator.geolocation.getCurrentPosition(
77+
resolve,
78+
reject
79+
);
80+
});
81+
ok(position, "Expected a position");
82+
iframe.contentWindow.navigator.geolocation.clearWatch(watchId);
83+
iframe.remove();
84+
}
85+
86+
resume_geolocationProvider(async () => {
87+
await new Promise(r => force_prompt(true, r));
88+
await runTest();
89+
SimpleTest.finish();
90+
});
91+
</script>
92+
</body>
93+
</html>

0 commit comments

Comments
 (0)
0