-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cid optout action #9952
Cid optout action #9952
Changes from 9 commits
79ac4b0
2fe1956
80f4f80
a89efd7
dbedc26
b89c765
1ce9310
bec5ce9
cad2804
55711d5
920176f
b59df1b
1127aa5
88b5979
a1d3bca
528416b
4db2704
954b1ed
3f03388
856e2d1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -157,6 +157,7 @@ export class AmpUserNotification extends AMP.BaseElement { | |
.registerUserNotification(this.elementId_, this); | ||
|
||
this.registerAction('dismiss', this.dismiss.bind(this)); | ||
this.registerAction('optoutOfCid', this.optoutOfCid_.bind(this)); | ||
} | ||
|
||
/** | ||
|
@@ -232,12 +233,27 @@ export class AmpUserNotification extends AMP.BaseElement { | |
} | ||
|
||
/** | ||
* Get async cid service. | ||
* Opts the user out of cid issuance and dismisses the notification. | ||
* @private | ||
*/ | ||
optoutOfCid_() { | ||
return this.getCidService_() | ||
.then(cid => cid.optOut()) | ||
.then(this.dismiss.bind(this), reason => { | ||
dev().error('amp-user-notification', | ||
'Failed to opt out of Cid', reason); | ||
// If optout fails, dismiss notification without persisting. | ||
this.dismiss(/*opt_forceNoPersist*/true); | ||
}); | ||
} | ||
|
||
/** | ||
* Get async cid. | ||
* @return {!Promise} | ||
* @private | ||
*/ | ||
getAsyncCid_() { | ||
return cidForDoc(this.element).then(cid => { | ||
return this.getCidService_().then(cid => { | ||
// `amp-user-notification` is our cid scope, while we give it a resolved | ||
// promise for the 2nd argument so that the 3rd argument (the | ||
// persistentConsent) is the one used to resolve getting | ||
|
@@ -252,6 +268,15 @@ export class AmpUserNotification extends AMP.BaseElement { | |
}); | ||
} | ||
|
||
/** | ||
* Get cid service. | ||
* @return {!Promise} | ||
* @private | ||
*/ | ||
getCidService_() { | ||
cidForDoc(this.element); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing return? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ugh, extension was not type-checked, added type checking (caught an unquoted JSON issues as well) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WAT |
||
} | ||
|
||
/** @override */ | ||
shouldShow() { | ||
return this.isDismissed().then(dismissed => { | ||
|
@@ -308,14 +333,17 @@ export class AmpUserNotification extends AMP.BaseElement { | |
/** | ||
* Hides the current user notification and invokes the `dialogResolve_` | ||
* method. Removes the `.amp-active` class from the element. | ||
* | ||
* @param {boolean=} opt_forceNoPersist If true, dismissal won't be persisted | ||
* regardless of 'data-persist-dismissal''s value | ||
*/ | ||
dismiss() { | ||
dismiss(opt_forceNoPersist = false) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Danger! This requires auditing every call to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wow good catch Justin. Only issue was Anyway now I am a bit worried about this. Should we start preferring explicit There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Type checking will often have a hard time finding these since the type of the arg will often be |
||
this.element.classList.remove('amp-active'); | ||
this.element.classList.add('amp-hidden'); | ||
this.dialogResolve_(); | ||
this.getViewport().removeFromFixedLayer(this.element); | ||
|
||
if (this.persistDismissal_) { | ||
if (this.persistDismissal_ && !opt_forceNoPersist) { | ||
// Store and post. | ||
this.storagePromise_.then(storage => { | ||
storage.set(this.storageKey_, true); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,7 +35,7 @@ import { | |
import {dict} from '../utils/object'; | ||
import {isIframed} from '../dom'; | ||
import {getCryptoRandomBytesArray} from '../utils/bytes'; | ||
import {viewerForDoc} from '../services'; | ||
import {viewerForDoc, storageForDoc} from '../services'; | ||
import {cryptoFor} from '../crypto'; | ||
import {parseJson, tryParseJson} from '../json'; | ||
import {timerFor} from '../services'; | ||
|
@@ -50,6 +50,11 @@ const BASE_CID_MAX_AGE_MILLIS = 365 * ONE_DAY_MILLIS; | |
|
||
const SCOPE_NAME_VALIDATOR = /^[a-zA-Z0-9-_.]+$/; | ||
|
||
const CID_OPTOUT_STORAGE_KEY = 'amp-cid-optout'; | ||
|
||
// TODO(aghassemi): replace with real viewer message when implemented. | ||
const CID_OPTOUT_VIEWER_MESSAGE = 'cidOptout'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @lannka Does viewer already have support for sending a message to inform user has opted out? For now I am just sending a message with this key There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, they're working on that. the key is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed the key to match |
||
|
||
/** | ||
* A base cid string value and the time it was last read / stored. | ||
* @typedef {{time: time, cid: string}} | ||
|
@@ -107,7 +112,8 @@ export class Cid { | |
* consent, of course). | ||
* @return {!Promise<?string>} A client identifier that should be used | ||
* within the current source origin and externalCidScope. Might be | ||
* null if no identifier was found or could be made. | ||
* null if user has opted out of cid or no identifier was found | ||
* or it could be made. | ||
* This promise may take a long time to resolve if consent isn't | ||
* given. | ||
*/ | ||
|
@@ -118,13 +124,64 @@ export class Cid { | |
'The CID scope and cookie name must only use the characters ' + | ||
'[a-zA-Z0-9-_.]+\nInstead found: %s', | ||
getCidStruct.scope); | ||
return consent.then(() => { | ||
return viewerForDoc(this.ampdoc).whenFirstVisible(); | ||
}).then(() => { | ||
return getExternalCid(this, getCidStruct, | ||
opt_persistenceConsent || consent); | ||
return isOptedOutOfCid(this.ampdoc).then(optedOut => { | ||
// TODO(aghassemi): null or '' ? | ||
if (optedOut) { | ||
return null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so, although There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. '' is better There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done, also moved the check after consent check (since user can optout while consent is pending) |
||
} else { | ||
return consent.then(() => { | ||
return viewerForDoc(this.ampdoc).whenFirstVisible(); | ||
}).then(() => { | ||
return getExternalCid(this, getCidStruct, | ||
opt_persistenceConsent || consent); | ||
}); | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* User will be opted out of Cid issuance for all scopes. | ||
* When opted-out Cid service will reject all `get` requests. | ||
* | ||
* @return {!Promise} | ||
*/ | ||
optOut() { | ||
return optOutOfCid(this.ampdoc); | ||
} | ||
} | ||
|
||
/** | ||
* User will be opted out of Cid issuance for all scopes. | ||
* When opted-out Cid service will reject all `get` requests. | ||
* | ||
* @return {!Promise} | ||
* @visibleForTesting | ||
*/ | ||
export function optOutOfCid(ampdoc) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. was this because you had some trouble with the tests? |
||
|
||
// Tell the viewer that user has opted out. | ||
viewerForDoc(ampdoc).sendMessage(CID_OPTOUT_VIEWER_MESSAGE); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does it make sense to move this little thing into cid-impl? so we have centralized place managing the CID APIs There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this whole thing is in amp-impl.js, did you mean somewhere else? |
||
|
||
// Store the optout bit in storage | ||
return storageForDoc(ampdoc).then(storage => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we want localStorage or storage service here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is right. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will user be able to ever opt-in again? is there such an API? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nope, do we need that @cramforce? |
||
return storage.set(CID_OPTOUT_STORAGE_KEY, true); | ||
}); | ||
} | ||
|
||
/** | ||
* Whether user has opted out of Cid issuance for all scopes. | ||
* | ||
* @param {!./ampdoc-impl.AmpDoc} ampdoc | ||
* @return {!Promise<boolean>} | ||
* @visibleForTesting | ||
*/ | ||
export function isOptedOutOfCid(ampdoc) { | ||
return storageForDoc(ampdoc).then(storage => { | ||
return storage.get(CID_OPTOUT_STORAGE_KEY).then(val => !!val); | ||
}).catch(() => { | ||
// If we fail to read the flag, assume not opted out. | ||
return false; | ||
}); | ||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seemed to me as the most reasonable thing to do if opting out fails, thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM