Skip to content
Snippets Groups Projects
Commit b186aff5 authored by Matt Woodrow's avatar Matt Woodrow
Browse files

Bug 1625366 - Remove support for custom violation callbacks on nsCSPContext...

Bug 1625366 - Remove support for custom violation callbacks on nsCSPContext since it should no longer be needed. r=ckerschb

Differential Revision: https://phabricator.services.mozilla.com/D69911

--HG--
extra : moz-landing-system : lando
parent 26bafdbf
No related branches found
No related tags found
No related merge requests found
...@@ -121,18 +121,6 @@ nsCSPContext::ShouldLoad(nsContentPolicyType aContentType, ...@@ -121,18 +121,6 @@ nsCSPContext::ShouldLoad(nsContentPolicyType aContentType,
nsIURI* aOriginalURIIfRedirect, nsIURI* aOriginalURIIfRedirect,
bool aSendViolationReports, const nsAString& aNonce, bool aSendViolationReports, const nsAString& aNonce,
bool aParserCreated, int16_t* outDecision) { bool aParserCreated, int16_t* outDecision) {
return ShouldLoad(AsyncReportViolationCallback(AsyncReportViolation),
aContentType, aCSPEventListener, aContentLocation,
aMimeTypeGuess, aOriginalURIIfRedirect,
aSendViolationReports, aNonce, aParserCreated, outDecision);
}
nsresult nsCSPContext::ShouldLoad(
const AsyncReportViolationCallback& aCallback,
nsContentPolicyType aContentType, nsICSPEventListener* aCSPEventListener,
nsIURI* aContentLocation, const nsACString& aMimeTypeGuess,
nsIURI* aOriginalURIIfRedirect, bool aSendViolationReports,
const nsAString& aNonce, bool aParserCreated, int16_t* outDecision) {
if (CSPCONTEXTLOGENABLED()) { if (CSPCONTEXTLOGENABLED()) {
CSPCONTEXTLOG(("nsCSPContext::ShouldLoad, aContentLocation: %s", CSPCONTEXTLOG(("nsCSPContext::ShouldLoad, aContentLocation: %s",
aContentLocation->GetSpecOrDefault().get())); aContentLocation->GetSpecOrDefault().get()));
...@@ -167,7 +155,7 @@ nsresult nsCSPContext::ShouldLoad( ...@@ -167,7 +155,7 @@ nsresult nsCSPContext::ShouldLoad(
} }
bool permitted = bool permitted =
permitsInternal(aCallback, dir, permitsInternal(dir,
nullptr, // aTriggeringElement nullptr, // aTriggeringElement
aCSPEventListener, aContentLocation, aCSPEventListener, aContentLocation,
aOriginalURIIfRedirect, aNonce, isPreload, aOriginalURIIfRedirect, aNonce, isPreload,
...@@ -190,12 +178,11 @@ nsresult nsCSPContext::ShouldLoad( ...@@ -190,12 +178,11 @@ nsresult nsCSPContext::ShouldLoad(
} }
bool nsCSPContext::permitsInternal( bool nsCSPContext::permitsInternal(
const AsyncReportViolationCallback& aCallback, CSPDirective aDir, CSPDirective aDir, Element* aTriggeringElement,
Element* aTriggeringElement, nsICSPEventListener* aCSPEventListener, nsICSPEventListener* aCSPEventListener, nsIURI* aContentLocation,
nsIURI* aContentLocation, nsIURI* aOriginalURIIfRedirect, nsIURI* aOriginalURIIfRedirect, const nsAString& aNonce, bool aIsPreload,
const nsAString& aNonce, bool aIsPreload, bool aSpecific, bool aSpecific, bool aSendViolationReports,
bool aSendViolationReports, bool aSendContentLocationInViolationReports, bool aSendContentLocationInViolationReports, bool aParserCreated) {
bool aParserCreated) {
EnsureIPCPoliciesRead(); EnsureIPCPoliciesRead();
bool permits = true; bool permits = true;
...@@ -224,18 +211,19 @@ bool nsCSPContext::permitsInternal( ...@@ -224,18 +211,19 @@ bool nsCSPContext::permitsInternal(
// If GetCallingLocation fails linenumber & columnNumber are set to 0 // If GetCallingLocation fails linenumber & columnNumber are set to 0
// anyway so we can skip checking if that is the case. // anyway so we can skip checking if that is the case.
} }
aCallback(this, aTriggeringElement, aCSPEventListener, AsyncReportViolation(
(aSendContentLocationInViolationReports ? aContentLocation aTriggeringElement, aCSPEventListener,
: nullptr), (aSendContentLocationInViolationReports ? aContentLocation
BlockedContentSource::eUnknown, /* a BlockedContentSource */ : nullptr),
aOriginalURIIfRedirect, /* in case of redirect originalURI is BlockedContentSource::eUnknown, /* a BlockedContentSource */
not null */ aOriginalURIIfRedirect, /* in case of redirect originalURI is not
violatedDirective, p, /* policy index */ null */
EmptyString(), /* no observer subject */ violatedDirective, p, /* policy index */
spec, /* source file */ EmptyString(), /* no observer subject */
EmptyString(), /* no script sample */ spec, /* source file */
lineNumber, /* line number */ EmptyString(), /* no script sample */
columnNumber); /* column number */ lineNumber, /* line number */
columnNumber); /* column number */
} }
} }
} }
...@@ -513,7 +501,7 @@ void nsCSPContext::reportInlineViolation( ...@@ -513,7 +501,7 @@ void nsCSPContext::reportInlineViolation(
columnNumber = aColumnNumber; columnNumber = aColumnNumber;
} }
AsyncReportViolation(this, aTriggeringElement, aCSPEventListener, AsyncReportViolation(aTriggeringElement, aCSPEventListener,
nullptr, // aBlockedURI nullptr, // aBlockedURI
BlockedContentSource::eInline, // aBlockedSource BlockedContentSource::eInline, // aBlockedSource
mSelfURI, // aOriginalURI mSelfURI, // aOriginalURI
...@@ -607,15 +595,6 @@ NS_IMETHODIMP ...@@ -607,15 +595,6 @@ NS_IMETHODIMP
nsCSPContext::GetAllowsNavigateTo(nsIURI* aURI, bool aIsFormSubmission, nsCSPContext::GetAllowsNavigateTo(nsIURI* aURI, bool aIsFormSubmission,
bool aWasRedirected, bool aEnforceWhitelist, bool aWasRedirected, bool aEnforceWhitelist,
bool* outAllowsNavigateTo) { bool* outAllowsNavigateTo) {
return GetAllowsNavigateTo(AsyncReportViolationCallback(AsyncReportViolation),
aURI, aIsFormSubmission, aWasRedirected,
aEnforceWhitelist, outAllowsNavigateTo);
}
nsresult nsCSPContext::GetAllowsNavigateTo(
const AsyncReportViolationCallback& aCallback, nsIURI* aURI,
bool aIsFormSubmission, bool aWasRedirected, bool aEnforceWhitelist,
bool* outAllowsNavigateTo) {
/* /*
* The matrix below shows the different values of (aWasRedirect, * The matrix below shows the different values of (aWasRedirect,
* aEnforceWhitelist) for the three different checks we do. * aEnforceWhitelist) for the three different checks we do.
...@@ -676,8 +655,7 @@ nsresult nsCSPContext::GetAllowsNavigateTo( ...@@ -676,8 +655,7 @@ nsresult nsCSPContext::GetAllowsNavigateTo(
} }
// Report the violation // Report the violation
nsresult rv = aCallback( nsresult rv = AsyncReportViolation(
this,
nullptr, // aTriggeringElement nullptr, // aTriggeringElement
nullptr, // aCSPEventListener nullptr, // aCSPEventListener
blockedURIForReporting, // aBlockedURI blockedURIForReporting, // aBlockedURI
...@@ -728,24 +706,24 @@ nsresult nsCSPContext::GetAllowsNavigateTo( ...@@ -728,24 +706,24 @@ nsresult nsCSPContext::GetAllowsNavigateTo(
* GetAllowsInline() and do not call this macro, hence we can pass 'false' * GetAllowsInline() and do not call this macro, hence we can pass 'false'
* as the argument _aParserCreated_ to allows(). * as the argument _aParserCreated_ to allows().
*/ */
#define CASE_CHECK_AND_REPORT(violationType, contentPolicyType, nonceOrHash, \ #define CASE_CHECK_AND_REPORT(violationType, contentPolicyType, nonceOrHash, \
keyword, observerTopic) \ keyword, observerTopic) \
case nsIContentSecurityPolicy::VIOLATION_TYPE_##violationType: \ case nsIContentSecurityPolicy::VIOLATION_TYPE_##violationType: \
PR_BEGIN_MACRO \ PR_BEGIN_MACRO \
if (!mPolicies[p]->allows(nsIContentPolicy::TYPE_##contentPolicyType, \ if (!mPolicies[p]->allows(nsIContentPolicy::TYPE_##contentPolicyType, \
keyword, nonceOrHash, false)) { \ keyword, nonceOrHash, false)) { \
nsAutoString violatedDirective; \ nsAutoString violatedDirective; \
bool reportSample = false; \ bool reportSample = false; \
mPolicies[p]->getDirectiveStringAndReportSampleForContentType( \ mPolicies[p]->getDirectiveStringAndReportSampleForContentType( \
nsIContentPolicy::TYPE_##contentPolicyType, violatedDirective, \ nsIContentPolicy::TYPE_##contentPolicyType, violatedDirective, \
&reportSample); \ &reportSample); \
AsyncReportViolation( \ AsyncReportViolation(aTriggeringElement, aCSPEventListener, nullptr, \
this, aTriggeringElement, aCSPEventListener, nullptr, \ blockedContentSource, nullptr, violatedDirective, \
blockedContentSource, nullptr, violatedDirective, p, \ p, NS_LITERAL_STRING(observerTopic), aSourceFile, \
NS_LITERAL_STRING(observerTopic), aSourceFile, \ reportSample ? aScriptSample : EmptyString(), \
reportSample ? aScriptSample : EmptyString(), aLineNum, aColumnNum); \ aLineNum, aColumnNum); \
} \ } \
PR_END_MACRO; \ PR_END_MACRO; \
break break
/** /**
...@@ -1520,25 +1498,24 @@ class CSPReportSenderRunnable final : public Runnable { ...@@ -1520,25 +1498,24 @@ class CSPReportSenderRunnable final : public Runnable {
* source column number of the violation (if available) * source column number of the violation (if available)
*/ */
nsresult nsCSPContext::AsyncReportViolation( nsresult nsCSPContext::AsyncReportViolation(
nsCSPContext* aContext, Element* aTriggeringElement, Element* aTriggeringElement, nsICSPEventListener* aCSPEventListener,
nsICSPEventListener* aCSPEventListener, nsIURI* aBlockedURI, nsIURI* aBlockedURI, BlockedContentSource aBlockedContentSource,
BlockedContentSource aBlockedContentSource, nsIURI* aOriginalURI, nsIURI* aOriginalURI, const nsAString& aViolatedDirective,
const nsAString& aViolatedDirective, uint32_t aViolatedPolicyIndex, uint32_t aViolatedPolicyIndex, const nsAString& aObserverSubject,
const nsAString& aObserverSubject, const nsAString& aSourceFile, const nsAString& aSourceFile, const nsAString& aScriptSample,
const nsAString& aScriptSample, uint32_t aLineNum, uint32_t aColumnNum) { uint32_t aLineNum, uint32_t aColumnNum) {
aContext->EnsureIPCPoliciesRead(); EnsureIPCPoliciesRead();
NS_ENSURE_ARG_MAX(aViolatedPolicyIndex, aContext->mPolicies.Length() - 1); NS_ENSURE_ARG_MAX(aViolatedPolicyIndex, mPolicies.Length() - 1);
nsCOMPtr<nsIRunnable> task = new CSPReportSenderRunnable( nsCOMPtr<nsIRunnable> task = new CSPReportSenderRunnable(
aTriggeringElement, aCSPEventListener, aBlockedURI, aBlockedContentSource, aTriggeringElement, aCSPEventListener, aBlockedURI, aBlockedContentSource,
aOriginalURI, aViolatedPolicyIndex, aOriginalURI, aViolatedPolicyIndex,
aContext->mPolicies[aViolatedPolicyIndex]->getReportOnlyFlag(), mPolicies[aViolatedPolicyIndex]->getReportOnlyFlag(), aViolatedDirective,
aViolatedDirective, aObserverSubject, aSourceFile, aScriptSample, aObserverSubject, aSourceFile, aScriptSample, aLineNum, aColumnNum, this);
aLineNum, aColumnNum, aContext);
if (XRE_IsContentProcess()) { if (XRE_IsContentProcess()) {
if (aContext->mEventTarget) { if (mEventTarget) {
aContext->mEventTarget->Dispatch(task.forget(), NS_DISPATCH_NORMAL); mEventTarget->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
return NS_OK; return NS_OK;
} }
} }
...@@ -1621,8 +1598,7 @@ nsCSPContext::PermitsAncestry(nsILoadInfo* aLoadInfo, ...@@ -1621,8 +1598,7 @@ nsCSPContext::PermitsAncestry(nsILoadInfo* aLoadInfo,
NS_SecurityCompareURIs(ancestorsArray[a], mSelfURI, true); NS_SecurityCompareURIs(ancestorsArray[a], mSelfURI, true);
bool permits = bool permits =
permitsInternal(AsyncReportViolation, // violation callback permitsInternal(nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE,
nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE,
nullptr, // triggering element nullptr, // triggering element
nullptr, // nsICSPEventListener nullptr, // nsICSPEventListener
ancestorsArray[a], ancestorsArray[a],
...@@ -1649,15 +1625,15 @@ nsCSPContext::Permits(Element* aTriggeringElement, ...@@ -1649,15 +1625,15 @@ nsCSPContext::Permits(Element* aTriggeringElement,
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
*outPermits = permitsInternal(AsyncReportViolation, aDir, aTriggeringElement, *outPermits =
aCSPEventListener, aURI, permitsInternal(aDir, aTriggeringElement, aCSPEventListener, aURI,
nullptr, // no original (pre-redirect) URI nullptr, // no original (pre-redirect) URI
EmptyString(), // no nonce EmptyString(), // no nonce
false, // not a preload. false, // not a preload.
aSpecific, aSpecific,
true, // send violation reports true, // send violation reports
true, // send blocked URI in violation reports true, // send blocked URI in violation reports
false); // not parser created false); // not parser created
if (CSPCONTEXTLOGENABLED()) { if (CSPCONTEXTLOGENABLED()) {
CSPCONTEXTLOG(("nsCSPContext::Permits, aUri: %s, aDir: %d, isAllowed: %s", CSPCONTEXTLOG(("nsCSPContext::Permits, aUri: %s, aDir: %d, isAllowed: %s",
......
...@@ -119,16 +119,8 @@ class nsCSPContext : public nsIContentSecurityPolicy { ...@@ -119,16 +119,8 @@ class nsCSPContext : public nsIContentSecurityPolicy {
eSelf, eSelf,
}; };
using AsyncReportViolationCallback = std::function<nsresult( nsresult AsyncReportViolation(
nsCSPContext* aContext, mozilla::dom::Element* aTriggeringElement, mozilla::dom::Element* aTriggeringElement,
nsICSPEventListener* aCSPEventListener, nsIURI* aBlockedURI,
BlockedContentSource aBlockedContentSource, nsIURI* aOriginalURI,
const nsAString& aViolatedDirective, uint32_t aViolatedPolicyIndex,
const nsAString& aObserverSubject, const nsAString& aSourceFile,
const nsAString& aScriptSample, uint32_t aLineNum, uint32_t aColumnNum)>;
static nsresult AsyncReportViolation(
nsCSPContext* aContext, mozilla::dom::Element* aTriggeringElement,
nsICSPEventListener* aCSPEventListener, nsIURI* aBlockedURI, nsICSPEventListener* aCSPEventListener, nsIURI* aBlockedURI,
BlockedContentSource aBlockedContentSource, nsIURI* aOriginalURI, BlockedContentSource aBlockedContentSource, nsIURI* aOriginalURI,
const nsAString& aViolatedDirective, uint32_t aViolatedPolicyIndex, const nsAString& aViolatedDirective, uint32_t aViolatedPolicyIndex,
...@@ -148,20 +140,6 @@ class nsCSPContext : public nsIContentSecurityPolicy { ...@@ -148,20 +140,6 @@ class nsCSPContext : public nsIContentSecurityPolicy {
0); 0);
} }
nsresult GetAllowsNavigateTo(const AsyncReportViolationCallback& aCallback,
nsIURI* aURI, bool aIsFormSubmission,
bool aWasRedirected, bool aEnforceWhitelist,
bool* outAllowsNavigateTo);
nsresult ShouldLoad(const AsyncReportViolationCallback& aCallback,
nsContentPolicyType aContentType,
nsICSPEventListener* aCSPEventListener,
nsIURI* aContentLocation,
const nsACString& aMimeTypeGuess,
nsIURI* aOriginalURIIfRedirect,
bool aSendViolationReports, const nsAString& aNonce,
bool aParserCreated, int16_t* outDecision);
void AddIPCPolicy(const mozilla::ipc::ContentSecurityPolicy& aPolicy); void AddIPCPolicy(const mozilla::ipc::ContentSecurityPolicy& aPolicy);
void SerializePolicies( void SerializePolicies(
nsTArray<mozilla::ipc::ContentSecurityPolicy>& aPolicies); nsTArray<mozilla::ipc::ContentSecurityPolicy>& aPolicies);
...@@ -169,8 +147,7 @@ class nsCSPContext : public nsIContentSecurityPolicy { ...@@ -169,8 +147,7 @@ class nsCSPContext : public nsIContentSecurityPolicy {
private: private:
void EnsureIPCPoliciesRead(); void EnsureIPCPoliciesRead();
bool permitsInternal(const AsyncReportViolationCallback& aCallback, bool permitsInternal(CSPDirective aDir,
CSPDirective aDir,
mozilla::dom::Element* aTriggeringElement, mozilla::dom::Element* aTriggeringElement,
nsICSPEventListener* aCSPEventListener, nsICSPEventListener* aCSPEventListener,
nsIURI* aContentLocation, nsIURI* aOriginalURIIfRedirect, nsIURI* aContentLocation, nsIURI* aOriginalURIIfRedirect,
......
...@@ -275,15 +275,6 @@ nsresult CSPService::ConsultCSPForRedirect(nsIURI* aOriginalURI, ...@@ -275,15 +275,6 @@ nsresult CSPService::ConsultCSPForRedirect(nsIURI* aOriginalURI,
nsIURI* aNewURI, nsIURI* aNewURI,
nsILoadInfo* aLoadInfo, nsILoadInfo* aLoadInfo,
Maybe<nsresult>& aCancelCode) { Maybe<nsresult>& aCancelCode) {
return ConsultCSPForRedirect(nsCSPContext::AsyncReportViolationCallback(
nsCSPContext::AsyncReportViolation),
aOriginalURI, aNewURI, aLoadInfo, aCancelCode);
}
nsresult CSPService::ConsultCSPForRedirect(
const nsCSPContext::AsyncReportViolationCallback& aCallback,
nsIURI* aOriginalURI, nsIURI* aNewURI, nsILoadInfo* aLoadInfo,
Maybe<nsresult>& aCancelCode) {
// Check CSP navigate-to // Check CSP navigate-to
// We need to enforce the CSP of the document that initiated the load, // We need to enforce the CSP of the document that initiated the load,
// which is the CSP to inherit. // which is the CSP to inherit.
...@@ -291,12 +282,10 @@ nsresult CSPService::ConsultCSPForRedirect( ...@@ -291,12 +282,10 @@ nsresult CSPService::ConsultCSPForRedirect(
aLoadInfo->GetCspToInherit(); aLoadInfo->GetCspToInherit();
if (cspToInherit) { if (cspToInherit) {
bool allowsNavigateTo = false; bool allowsNavigateTo = false;
nsresult rv = static_cast<nsCSPContext*>(cspToInherit.get()) nsresult rv = cspToInherit->GetAllowsNavigateTo(
->GetAllowsNavigateTo(aCallback, aNewURI, aNewURI, aLoadInfo->GetIsFormSubmission(), true, /* aWasRedirected */
aLoadInfo->GetIsFormSubmission(), false, /* aEnforceWhitelist */
true, /* aWasRedirected */ &allowsNavigateTo);
false, /* aEnforceWhitelist */
&allowsNavigateTo);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
if (!allowsNavigateTo) { if (!allowsNavigateTo) {
...@@ -342,15 +331,14 @@ nsresult CSPService::ConsultCSPForRedirect( ...@@ -342,15 +331,14 @@ nsresult CSPService::ConsultCSPForRedirect(
nsCOMPtr<nsIContentSecurityPolicy> preloadCsp = aLoadInfo->GetPreloadCsp(); nsCOMPtr<nsIContentSecurityPolicy> preloadCsp = aLoadInfo->GetPreloadCsp();
if (preloadCsp) { if (preloadCsp) {
// Pass originalURI to indicate the redirect // Pass originalURI to indicate the redirect
static_cast<nsCSPContext*>(preloadCsp.get()) preloadCsp->ShouldLoad(
->ShouldLoad(aCallback, policyType, // load type per nsIContentPolicy (uint32_t)
policyType, // load type per nsIContentPolicy (uint32_t) cspEventListener,
cspEventListener, aNewURI, // nsIURI
aNewURI, // nsIURI EmptyCString(), // ACString - MIME guess
EmptyCString(), // ACString - MIME guess aOriginalURI, // Original nsIURI
aOriginalURI, // Original nsIURI true, // aSendViolationReports
true, // aSendViolationReports cspNonce, // nonce
cspNonce, // nonce
parserCreatedScript, &decision); parserCreatedScript, &decision);
// if the preload policy already denied the load, then there // if the preload policy already denied the load, then there
...@@ -366,15 +354,13 @@ nsresult CSPService::ConsultCSPForRedirect( ...@@ -366,15 +354,13 @@ nsresult CSPService::ConsultCSPForRedirect(
nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadInfo->GetCsp(); nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadInfo->GetCsp();
if (csp) { if (csp) {
// Pass originalURI to indicate the redirect // Pass originalURI to indicate the redirect
static_cast<nsCSPContext*>(csp.get())->ShouldLoad( csp->ShouldLoad(policyType, // load type per nsIContentPolicy (uint32_t)
aCallback, cspEventListener,
policyType, // load type per nsIContentPolicy (uint32_t) aNewURI, // nsIURI
cspEventListener, EmptyCString(), // ACString - MIME guess
aNewURI, // nsIURI aOriginalURI, // Original nsIURI
EmptyCString(), // ACString - MIME guess true, // aSendViolationReports
aOriginalURI, // Original nsIURI cspNonce, // nonce
true, // aSendViolationReports
cspNonce, // nonce
parserCreatedScript, &decision); parserCreatedScript, &decision);
if (NS_CP_REJECTED(decision)) { if (NS_CP_REJECTED(decision)) {
aCancelCode = Some(NS_ERROR_DOM_BAD_URI); aCancelCode = Some(NS_ERROR_DOM_BAD_URI);
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
#include "nsIChannel.h" #include "nsIChannel.h"
#include "nsIChannelEventSink.h" #include "nsIChannelEventSink.h"
#include "nsDataHashtable.h" #include "nsDataHashtable.h"
#include "nsCSPContext.h"
#define CSPSERVICE_CONTRACTID "@mozilla.org/cspservice;1" #define CSPSERVICE_CONTRACTID "@mozilla.org/cspservice;1"
#define CSPSERVICE_CID \ #define CSPSERVICE_CID \
...@@ -43,11 +42,6 @@ class CSPService : public nsIContentPolicy, public nsIChannelEventSink { ...@@ -43,11 +42,6 @@ class CSPService : public nsIContentPolicy, public nsIChannelEventSink {
nsILoadInfo* aLoadInfo, nsILoadInfo* aLoadInfo,
Maybe<nsresult>& aCancelCode); Maybe<nsresult>& aCancelCode);
static nsresult ConsultCSPForRedirect(
const nsCSPContext::AsyncReportViolationCallback& aCallback,
nsIURI* aOriginalURI, nsIURI* aNewURI, nsILoadInfo* aLoadInfo,
Maybe<nsresult>& aCancelCode);
protected: protected:
virtual ~CSPService(); virtual ~CSPService();
}; };
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment