| /* |
| * Copyright (C) 2019 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "DocumentStorageAccess.h" |
| |
| #if ENABLE(RESOURCE_LOAD_STATISTICS) |
| |
| #include "Chrome.h" |
| #include "ChromeClient.h" |
| #include "Document.h" |
| #include "Frame.h" |
| #include "FrameLoader.h" |
| #include "FrameLoaderClient.h" |
| #include "JSDOMPromiseDeferred.h" |
| #include "Microtasks.h" |
| #include "Page.h" |
| #include "RegistrableDomain.h" |
| #include "SecurityOrigin.h" |
| #include "Settings.h" |
| #include "UserGestureIndicator.h" |
| |
| namespace WebCore { |
| |
| DocumentStorageAccess* DocumentStorageAccess::from(Document& document) |
| { |
| auto* supplement = static_cast<DocumentStorageAccess*>(Supplement<Document>::from(&document, supplementName())); |
| if (!supplement) { |
| auto newSupplement = makeUnique<DocumentStorageAccess>(document); |
| supplement = newSupplement.get(); |
| provideTo(&document, supplementName(), WTFMove(newSupplement)); |
| } |
| return supplement; |
| } |
| |
| const char* DocumentStorageAccess::supplementName() |
| { |
| return "DocumentStorageAccess"; |
| } |
| |
| void DocumentStorageAccess::hasStorageAccess(Document& document, Ref<DeferredPromise>&& promise) |
| { |
| DocumentStorageAccess::from(document)->hasStorageAccess(WTFMove(promise)); |
| } |
| |
| void DocumentStorageAccess::requestStorageAccess(Document& document, Ref<DeferredPromise>&& promise) |
| { |
| DocumentStorageAccess::from(document)->requestStorageAccess(WTFMove(promise)); |
| } |
| |
| void DocumentStorageAccess::hasStorageAccess(Ref<DeferredPromise>&& promise) |
| { |
| ASSERT(m_document.settings().storageAccessAPIEnabled()); |
| |
| auto* frame = m_document.frame(); |
| if (frame && hasFrameSpecificStorageAccess()) { |
| promise->resolve<IDLBoolean>(true); |
| return; |
| } |
| |
| if (!frame || m_document.securityOrigin().isUnique()) { |
| promise->resolve<IDLBoolean>(false); |
| return; |
| } |
| |
| if (frame->isMainFrame()) { |
| promise->resolve<IDLBoolean>(true); |
| return; |
| } |
| |
| auto& securityOrigin = m_document.securityOrigin(); |
| auto& topSecurityOrigin = m_document.topDocument().securityOrigin(); |
| if (securityOrigin.equal(&topSecurityOrigin)) { |
| promise->resolve<IDLBoolean>(true); |
| return; |
| } |
| |
| auto* page = frame->page(); |
| if (!page) { |
| promise->reject(); |
| return; |
| } |
| |
| auto subFrameDomain = RegistrableDomain::uncheckedCreateFromHost(securityOrigin.host()); |
| auto topFrameDomain = RegistrableDomain::uncheckedCreateFromHost(topSecurityOrigin.host()); |
| page->chrome().client().hasStorageAccess(WTFMove(subFrameDomain), WTFMove(topFrameDomain), *frame, [weakThis = makeWeakPtr(*this), promise = WTFMove(promise)] (bool hasAccess) { |
| if (!weakThis) |
| return; |
| |
| promise->resolve<IDLBoolean>(hasAccess); |
| }); |
| } |
| |
| void DocumentStorageAccess::requestStorageAccess(Ref<DeferredPromise>&& promise) |
| { |
| ASSERT(m_document.settings().storageAccessAPIEnabled()); |
| |
| auto* frame = m_document.frame(); |
| if (frame && hasFrameSpecificStorageAccess()) { |
| promise->resolve(); |
| return; |
| } |
| |
| if (!frame || m_document.securityOrigin().isUnique() || !isAllowedToRequestFrameSpecificStorageAccess()) { |
| promise->reject(); |
| return; |
| } |
| |
| if (frame->isMainFrame()) { |
| promise->resolve(); |
| return; |
| } |
| |
| auto& topDocument = m_document.topDocument(); |
| auto& topSecurityOrigin = topDocument.securityOrigin(); |
| auto& securityOrigin = m_document.securityOrigin(); |
| if (securityOrigin.equal(&topSecurityOrigin)) { |
| promise->resolve(); |
| return; |
| } |
| |
| // If there is a sandbox, it has to allow the storage access API to be called. |
| if (m_document.sandboxFlags() != SandboxNone && m_document.isSandboxed(SandboxStorageAccessByUserActivation)) { |
| promise->reject(); |
| return; |
| } |
| |
| // The iframe has to be a direct child of the top document. |
| if (&topDocument != m_document.parentDocument()) { |
| promise->reject(); |
| return; |
| } |
| |
| if (!UserGestureIndicator::processingUserGesture()) { |
| promise->reject(); |
| return; |
| } |
| |
| auto* page = frame->page(); |
| if (!page) { |
| promise->reject(); |
| return; |
| } |
| |
| auto subFrameDomain = RegistrableDomain::uncheckedCreateFromHost(securityOrigin.host()); |
| auto topFrameDomain = RegistrableDomain::uncheckedCreateFromHost(topSecurityOrigin.host()); |
| |
| page->chrome().client().requestStorageAccess(WTFMove(subFrameDomain), WTFMove(topFrameDomain), *frame, [this, weakThis = makeWeakPtr(*this), promise = WTFMove(promise)] (StorageAccessWasGranted wasGranted, StorageAccessPromptWasShown promptWasShown) mutable { |
| if (!weakThis) |
| return; |
| |
| // Consume the user gesture only if the user explicitly denied access. |
| bool shouldPreserveUserGesture = wasGranted == StorageAccessWasGranted::Yes || promptWasShown == StorageAccessPromptWasShown::No; |
| |
| if (shouldPreserveUserGesture) { |
| MicrotaskQueue::mainThreadQueue().append(makeUnique<VoidMicrotask>([this, weakThis] () { |
| if (weakThis) |
| enableTemporaryTimeUserGesture(); |
| })); |
| } |
| |
| if (wasGranted == StorageAccessWasGranted::Yes) |
| promise->resolve(); |
| else { |
| if (promptWasShown == StorageAccessPromptWasShown::Yes) |
| setWasExplicitlyDeniedFrameSpecificStorageAccess(); |
| promise->reject(); |
| } |
| |
| if (shouldPreserveUserGesture) { |
| MicrotaskQueue::mainThreadQueue().append(makeUnique<VoidMicrotask>([this, weakThis = WTFMove(weakThis)] () { |
| if (weakThis) |
| consumeTemporaryTimeUserGesture(); |
| })); |
| } |
| }); |
| } |
| |
| void DocumentStorageAccess::enableTemporaryTimeUserGesture() |
| { |
| m_temporaryUserGesture = makeUnique<UserGestureIndicator>(ProcessingUserGesture, &m_document); |
| } |
| |
| void DocumentStorageAccess::consumeTemporaryTimeUserGesture() |
| { |
| m_temporaryUserGesture = nullptr; |
| } |
| |
| bool DocumentStorageAccess::hasFrameSpecificStorageAccess() const |
| { |
| auto* frame = m_document.frame(); |
| return frame && frame->loader().client().hasFrameSpecificStorageAccess(); |
| } |
| |
| } // namespace WebCore |
| |
| #endif // ENABLE(RESOURCE_LOAD_STATISTICS) |