blob: 20bfefeec4f68307229b6eb12cf07f492f897b9a [file] [log] [blame]
/*
* Copyright (C) 2025 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 "DocumentMediaElement.h"
#if ENABLE(VIDEO)
#include "CommonVM.h"
#include "DOMWrapperWorld.h"
#include "Document.h"
#include "FrameDestructionObserverInlines.h"
#include "RenderTheme.h"
#include "ScriptController.h"
#include "ScriptSourceCode.h"
#include <JavaScriptCore/JSCInlines.h>
#include <JavaScriptCore/JSLock.h>
#include <JavaScriptCore/TopExceptionScope.h>
#include <wtf/TZoneMallocInlines.h>
namespace WebCore {
WTF_MAKE_TZONE_ALLOCATED_IMPL(DocumentMediaElement);
DocumentMediaElement& DocumentMediaElement::from(Document& document)
{
auto* supplement = downcast<DocumentMediaElement>(Supplement<Document>::from(&document, supplementName()));
if (!supplement) {
auto newSupplement = makeUnique<DocumentMediaElement>(document);
supplement = newSupplement.get();
provideTo(&document, supplementName(), WTF::move(newSupplement));
}
return *supplement;
}
DocumentMediaElement::DocumentMediaElement(Document& document)
: m_document { document }
{
}
Document& DocumentMediaElement::document() const
{
return m_document;
}
ASCIILiteral DocumentMediaElement::supplementName()
{
return "DocumentMediaElement"_s;
};
bool DocumentMediaElement::setupAndCallMediaControlsJS(NOESCAPE const JSSetupFunction& task)
{
if (!ensureMediaControlsScript())
return false;
return setupAndCallJS(task);
}
DOMWrapperWorld& DocumentMediaElement::ensureIsolatedWorld()
{
if (!m_isolatedWorld) {
m_isolatedWorld = DOMWrapperWorld::create(Ref { commonVM() }, DOMWrapperWorld::Type::Internal, "Media Controls (Document)"_s);
m_isolatedWorld->setIsMediaControls();
}
return *m_isolatedWorld;
}
bool DocumentMediaElement::ensureMediaControlsScript()
{
if (m_haveParsedMediaControlsScript)
return true;
Ref document = this->document();
auto mediaControlsScripts = RenderTheme::singleton().mediaControlsScripts();
if (mediaControlsScripts.isEmpty() || document->activeDOMObjectsAreSuspended() || document->activeDOMObjectsAreStopped())
return false;
m_haveParsedMediaControlsScript = setupAndCallJS([mediaControlsScripts = WTF::move(mediaControlsScripts)](JSDOMGlobalObject& globalObject, JSC::JSGlobalObject&, ScriptController& scriptController, DOMWrapperWorld& world) {
auto& vm = globalObject.vm();
auto scope = DECLARE_THROW_SCOPE(vm);
for (auto& mediaControlsScript : mediaControlsScripts) {
if (mediaControlsScript.isEmpty())
continue;
scriptController.evaluateInWorldIgnoringException(ScriptSourceCode(mediaControlsScript, JSC::SourceTaintedOrigin::Untainted), world);
RETURN_IF_EXCEPTION(scope, false);
}
return true;
});
return m_haveParsedMediaControlsScript;
}
bool DocumentMediaElement::setupAndCallJS(NOESCAPE const JSSetupFunction& task)
{
Ref world = ensureIsolatedWorld();
Ref protectedDocument = this->document();
RefPtr protectedFrame = protectedDocument->frame();
if (!protectedFrame)
return false;
CheckedRef scriptController = protectedFrame->script();
auto* globalObject = JSC::jsCast<JSDOMGlobalObject*>(scriptController->globalObject(world));
if (!globalObject)
return false;
auto& vm = globalObject->vm();
JSC::JSLockHolder lock(vm);
auto scope = DECLARE_TOP_EXCEPTION_SCOPE(vm);
auto* lexicalGlobalObject = globalObject;
auto reportExceptionAndReturnFalse = [&] () -> bool {
auto* exception = scope.exception();
scope.clearException();
reportException(globalObject, exception);
return false;
};
auto result = task(*globalObject, *lexicalGlobalObject, scriptController, world);
RETURN_IF_EXCEPTION(scope, reportExceptionAndReturnFalse());
return result;
}
}
#endif