diff options
Diffstat (limited to '')
-rw-r--r-- | scripting/source/provider/MasterScriptProvider.cxx | 676 |
1 files changed, 676 insertions, 0 deletions
diff --git a/scripting/source/provider/MasterScriptProvider.cxx b/scripting/source/provider/MasterScriptProvider.cxx new file mode 100644 index 000000000..93d6a60f3 --- /dev/null +++ b/scripting/source/provider/MasterScriptProvider.cxx @@ -0,0 +1,676 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <comphelper/SetFlagContextHelper.hxx> +#include <comphelper/documentinfo.hxx> + +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <tools/diagnose_ex.h> +#include <tools/urlobj.hxx> + +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/script/provider/ScriptFrameworkErrorException.hpp> +#include <com/sun/star/uri/XUriReference.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp> + +#include <com/sun/star/deployment/XPackage.hpp> +#include <com/sun/star/script/browse/BrowseNodeTypes.hpp> +#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp> +#include <com/sun/star/script/provider/ScriptFrameworkErrorType.hpp> + +#include <util/MiscUtils.hxx> +#include <sal/log.hxx> + +#include "MasterScriptProvider.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::script; +using namespace ::com::sun::star::document; +using namespace ::sf_misc; + +namespace func_provider +{ + +static bool endsWith( const OUString& target, const OUString& item ) +{ + sal_Int32 index = target.indexOf( item ); + return index != -1 && + index == ( target.getLength() - item.getLength() ); +} + +/* should be available in some central location. */ + +// XScriptProvider implementation + + +MasterScriptProvider::MasterScriptProvider( const Reference< XComponentContext > & xContext ): + m_xContext( xContext ), m_bIsValid( false ), m_bInitialised( false ), + m_bIsPkgMSP( false ) +{ + ENSURE_OR_THROW( m_xContext.is(), "MasterScriptProvider::MasterScriptProvider: No context available\n" ); + m_xMgr = m_xContext->getServiceManager(); + ENSURE_OR_THROW( m_xMgr.is(), "MasterScriptProvider::MasterScriptProvider: No service manager available\n" ); + m_bIsValid = true; +} + + +MasterScriptProvider::~MasterScriptProvider() +{ +} + + +void SAL_CALL MasterScriptProvider::initialize( const Sequence < Any >& args ) +{ + if ( m_bInitialised ) + return; + + m_bIsValid = false; + + sal_Int32 len = args.getLength(); + if ( len > 1 ) + { + throw RuntimeException( + "MasterScriptProvider::initialize: invalid number of arguments" ); + } + + Sequence< Any > invokeArgs( len ); + + if ( len != 0 ) + { + auto pinvokeArgs = invokeArgs.getArray(); + // check if first parameter is a string + // if it is, this implies that this is a MSP created + // with a user or share ctx ( used for browse functionality ) + + if ( args[ 0 ] >>= m_sCtxString ) + { + pinvokeArgs[ 0 ] = args[ 0 ]; + if ( m_sCtxString.startsWith( "vnd.sun.star.tdoc" ) ) + { + m_xModel = MiscUtils::tDocUrlToModel( m_sCtxString ); + } + } + else if ( args[ 0 ] >>= m_xInvocationContext ) + { + m_xModel.set( m_xInvocationContext->getScriptContainer(), UNO_QUERY_THROW ); + } + else + { + args[ 0 ] >>= m_xModel; + } + + if ( m_xModel.is() ) + { + // from the arguments, we were able to deduce a model. That alone doesn't + // suffice, we also need an XEmbeddedScripts which actually indicates support + // for embedding scripts + Reference< XEmbeddedScripts > xScripts( m_xModel, UNO_QUERY ); + if ( !xScripts.is() ) + { + throw lang::IllegalArgumentException( + "The given document does not support embedding scripts into it, and cannot be associated with such a document.", + *this, + 1 + ); + } + + try + { + m_sCtxString = MiscUtils::xModelToTdocUrl( m_xModel, m_xContext ); + } + catch ( const Exception& ) + { + Any aError( ::cppu::getCaughtException() ); + + Exception aException; + aError >>= aException; + OUString buf = + "MasterScriptProvider::initialize: caught " + + aError.getValueTypeName() + + ":" + + aException.Message; + throw lang::WrappedTargetException( buf, *this, aError ); + } + + if ( m_xInvocationContext.is() && m_xInvocationContext != m_xModel ) + pinvokeArgs[ 0 ] <<= m_xInvocationContext; + else + pinvokeArgs[ 0 ] <<= m_sCtxString; + } + + OUString pkgSpec = "uno_packages"; + sal_Int32 indexOfPkgSpec = m_sCtxString.lastIndexOf( pkgSpec ); + + // if context string ends with "uno_packages" + if ( indexOfPkgSpec > -1 && m_sCtxString.match( pkgSpec, indexOfPkgSpec ) ) + { + m_bIsPkgMSP = true; + } + else + { + m_bIsPkgMSP = false; + } + } + else // no args + { + // use either scripting context or maybe zero args? + invokeArgs = Sequence< Any >( 0 ); // no arguments + } + m_sAargs = invokeArgs; + // don't create pkg mgr MSP for documents, not supported + if ( !m_bIsPkgMSP && !m_xModel.is() ) + { + createPkgProvider(); + } + + m_bInitialised = true; + m_bIsValid = true; +} + + +void MasterScriptProvider::createPkgProvider() +{ + try + { + Any location; + location <<= m_sCtxString + ":uno_packages"; + + Reference< provider::XScriptProviderFactory > xFac = + provider::theMasterScriptProviderFactory::get( m_xContext ); + + m_xMSPPkg.set( + xFac->createScriptProvider( location ), UNO_SET_THROW ); + + } + catch ( const Exception& ) + { + TOOLS_WARN_EXCEPTION("scripting.provider", "Exception creating MasterScriptProvider for uno_packages in context " + << m_sCtxString ); + } +} + + +Reference< provider::XScript > +MasterScriptProvider::getScript( const OUString& scriptURI ) +{ + if ( !m_bIsValid ) + { + throw provider::ScriptFrameworkErrorException( + "MasterScriptProvider not initialised", Reference< XInterface >(), + scriptURI, "", + provider::ScriptFrameworkErrorType::UNKNOWN ); + } + + // need to get the language from the string + + Reference< uri::XUriReferenceFactory > xFac ( uri::UriReferenceFactory::create( m_xContext ) ); + + Reference< uri::XUriReference > uriRef = xFac->parse( scriptURI ); + + Reference < uri::XVndSunStarScriptUrl > sfUri( uriRef, UNO_QUERY ); + + if ( !uriRef.is() || !sfUri.is() ) + { + throw provider::ScriptFrameworkErrorException( + "Incorrect format for Script URI: " + scriptURI, + Reference< XInterface >(), + scriptURI, "", + provider::ScriptFrameworkErrorType::UNKNOWN ); + } + + OUString langKey("language"); + OUString locKey("location"); + + if ( !sfUri->hasParameter( langKey ) || + !sfUri->hasParameter( locKey ) || + ( sfUri->getName().isEmpty() ) ) + { + throw provider::ScriptFrameworkErrorException( + "Incorrect format for Script URI: " + scriptURI, + Reference< XInterface >(), + scriptURI, "", + provider::ScriptFrameworkErrorType::UNKNOWN ); + } + + OUString language = sfUri->getParameter( langKey ); + OUString location = sfUri->getParameter( locKey ); + + // if script us located in uno pkg + sal_Int32 index = -1; + OUString pkgTag(":uno_packages"); + // for languages other than basic, scripts located in uno packages + // are merged into the user/share location context. + // For other languages the location attribute in script url has the form + // location = [user|share]:uno_packages or location :uno_packages/xxxx.uno.pkg + // we need to extract the value of location part from the + // location attribute of the script, if the script is located in an + // uno package then that is the location part up to and including + // ":uno_packages", if the script is not in a uno package then the + // normal value is used e.g. user or share. + // The value extracted will be used to determine if the script is + // located in the same location context as this MSP. + // For Basic, the language script provider can handle the execution of a + // script in any location context + if ( ( index = location.indexOf( pkgTag ) ) > -1 ) + { + location = location.copy( 0, index + pkgTag.getLength() ); + } + + Reference< provider::XScript > xScript; + + // If the script location is in the same location context as this + // MSP then delete to the language provider controlled by this MSP + // ** Special case is BASIC, all calls to getScript will be handled + // by the language script provider in the current location context + // even if it's different + if ( ( location == "document" + && m_xModel.is() + ) + || ( endsWith( m_sCtxString, location ) ) + || ( language == "Basic" ) + ) + { + Reference< provider::XScriptProvider > xScriptProvider; + OUString serviceName = "com.sun.star.script.provider.ScriptProviderFor" + language; + if ( !providerCache() ) + { + throw provider::ScriptFrameworkErrorException( + "No LanguageProviders detected", + Reference< XInterface >(), + sfUri->getName(), language, + provider::ScriptFrameworkErrorType::NOTSUPPORTED ); + } + + try + { + xScriptProvider.set( + providerCache()->getProvider( serviceName ), + UNO_SET_THROW ); + } + catch( const Exception& e ) + { + throw provider::ScriptFrameworkErrorException( + e.Message, Reference< XInterface >(), + sfUri->getName(), language, + provider::ScriptFrameworkErrorType::NOTSUPPORTED ); + } + + xScript=xScriptProvider->getScript( scriptURI ); + } + else + { + Reference< provider::XScriptProviderFactory > xFac_ = + provider::theMasterScriptProviderFactory::get( m_xContext ); + + Reference< provider::XScriptProvider > xSP( + xFac_->createScriptProvider( Any( location ) ), UNO_SET_THROW ); + xScript = xSP->getScript( scriptURI ); + } + + return xScript; +} + + +ProviderCache* +MasterScriptProvider::providerCache() +{ + if ( !m_pPCache ) + { + std::scoped_lock aGuard( m_mutex ); + if ( !m_pPCache ) + { + Sequence<OUString> denylist { "com.sun.star.script.provider.ScriptProviderForBasic" }; + + if ( !m_bIsPkgMSP ) + { + m_pPCache.reset( new ProviderCache( m_xContext, m_sAargs ) ); + } + else + { + m_pPCache.reset( new ProviderCache( m_xContext, m_sAargs, denylist ) ); + } + } + } + return m_pPCache.get(); +} + + +OUString SAL_CALL +MasterScriptProvider::getName() +{ + if ( !m_bIsPkgMSP ) + { + OUString sCtx = getContextString(); + if ( sCtx.startsWith( "vnd.sun.star.tdoc" ) ) + { + Reference< frame::XModel > xModel = m_xModel; + if ( !xModel.is() ) + { + xModel = MiscUtils::tDocUrlToModel( sCtx ); + } + + m_sNodeName = ::comphelper::DocumentInfo::getDocumentTitle( xModel ); + } + else + { + m_sNodeName = parseLocationName( getContextString() ); + } + } + else + { + m_sNodeName = "uno_packages"; + } + return m_sNodeName; +} + + +Sequence< Reference< browse::XBrowseNode > > SAL_CALL +MasterScriptProvider::getChildNodes() +{ + Sequence< Reference< provider::XScriptProvider > > providers = providerCache()->getAllProviders(); + + sal_Int32 size = providers.getLength(); + bool hasPkgs = m_xMSPPkg.is(); + if ( hasPkgs ) + { + size++; + } + Sequence< Reference< browse::XBrowseNode > > children( size ); + auto childrenRange = asNonConstRange(children); + sal_Int32 provIndex = 0; + for ( ; provIndex < providers.getLength(); provIndex++ ) + { + childrenRange[ provIndex ].set( providers[ provIndex ], UNO_QUERY ); + } + + if ( hasPkgs ) + { + childrenRange[ provIndex ].set( m_xMSPPkg, UNO_QUERY ); + + } + + return children; +} + + +sal_Bool SAL_CALL +MasterScriptProvider::hasChildNodes() +{ + return true; +} + + +sal_Int16 SAL_CALL +MasterScriptProvider::getType() +{ + return browse::BrowseNodeTypes::CONTAINER; +} + + +OUString +MasterScriptProvider::parseLocationName( const OUString& location ) +{ + // strip out the last leaf of location name + // e.g. file://dir1/dir2/Blah.sxw - > Blah.sxw + OUString temp = location; + INetURLObject aURLObj( temp ); + if ( !aURLObj.HasError() ) + temp = aURLObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset ); + return temp; +} + +namespace +{ +template <typename Proc> bool FindProviderAndApply(ProviderCache& rCache, Proc p) +{ + auto pass = [&rCache, &p]() -> bool + { + bool bResult = false; + const Sequence<Reference<provider::XScriptProvider>> aAllProviders = rCache.getAllProviders(); + for (const auto& rProv : aAllProviders) + { + Reference<container::XNameContainer> xCont(rProv, UNO_QUERY); + if (!xCont.is()) + { + continue; + } + try + { + bResult = p(xCont); + if (bResult) + break; + } + catch (const Exception&) + { + TOOLS_INFO_EXCEPTION("scripting.provider", "ignoring"); + } + } + return bResult; + }; + bool bSuccess = false; + // 1. Try to perform the operation without trying to enable JVM (if disabled) + // This allows us to avoid useless user interaction in case when other provider + // (not JVM) actually handles the operation. + { + css::uno::ContextLayer layer(comphelper::NoEnableJavaInteractionContext()); + bSuccess = pass(); + } + // 2. Now retry asking to enable JVM in case we didn't succeed first time + if (!bSuccess) + { + bSuccess = pass(); + } + return bSuccess; +} +} // namespace + +// Register Package +void SAL_CALL +MasterScriptProvider::insertByName( const OUString& aName, const Any& aElement ) +{ + if ( !m_bIsPkgMSP ) + { + if ( !m_xMSPPkg.is() ) + { + throw RuntimeException( "PackageMasterScriptProvider is unitialised" ); + } + + Reference< container::XNameContainer > xCont( m_xMSPPkg, UNO_QUERY_THROW ); + xCont->insertByName( aName, aElement ); + } + else + { + Reference< deployment::XPackage > xPkg( aElement, UNO_QUERY ); + if ( !xPkg.is() ) + { + throw lang::IllegalArgumentException( "Couldn't convert to XPackage", + Reference < XInterface > (), 2 ); + } + if ( aName.isEmpty() ) + { + throw lang::IllegalArgumentException( "Name not set!!", + Reference < XInterface > (), 1 ); + } + // TODO for library package parse the language, for the moment will try + // to get each provider to process the new Package, the first one the succeeds + // will terminate processing + const bool bSuccess = FindProviderAndApply( + *providerCache(), [&aName, &aElement](Reference<container::XNameContainer>& xCont) { + xCont->insertByName(aName, aElement); + return true; + }); + if (!bSuccess) + { + // No script providers could process the package + throw lang::IllegalArgumentException( "Failed to register package for " + aName, + Reference < XInterface > (), 2 ); + } + } +} + + +// Revoke Package +void SAL_CALL +MasterScriptProvider::removeByName( const OUString& Name ) +{ + if ( !m_bIsPkgMSP ) + { + if ( !m_xMSPPkg.is() ) + { + throw RuntimeException( "PackageMasterScriptProvider is unitialised" ); + } + + Reference< container::XNameContainer > xCont( m_xMSPPkg, UNO_QUERY_THROW ); + xCont->removeByName( Name ); + } + else + { + if ( Name.isEmpty() ) + { + throw lang::IllegalArgumentException( "Name not set!!", + Reference < XInterface > (), 1 ); + } + // TODO for Script library package url parse the language, + // for the moment will just try to get each provider to process remove/revoke + // request, the first one the succeeds will terminate processing + const bool bSuccess = FindProviderAndApply( + *providerCache(), [&Name](Reference<container::XNameContainer>& xCont) { + xCont->removeByName(Name); + return true; + }); + if (!bSuccess) + { + // No script providers could process the package + throw lang::IllegalArgumentException( "Failed to revoke package for " + Name, + Reference < XInterface > (), 1 ); + } + + } +} + + +void SAL_CALL +MasterScriptProvider::replaceByName( const OUString& /*aName*/, const Any& /*aElement*/ ) +{ + // TODO needs implementing + throw RuntimeException( "replaceByName not implemented!!!!" ); +} + +Any SAL_CALL +MasterScriptProvider::getByName( const OUString& /*aName*/ ) +{ + // TODO needs to be implemented + throw RuntimeException( "getByName not implemented!!!!" ); +} + +sal_Bool SAL_CALL +MasterScriptProvider::hasByName( const OUString& aName ) +{ + bool result = false; + if ( !m_bIsPkgMSP ) + { + if ( m_xMSPPkg.is() ) + { + Reference< container::XNameContainer > xCont( m_xMSPPkg, UNO_QUERY_THROW ); + result = xCont->hasByName( aName ); + } + // If this is a document provider then we shouldn't + // have a PackageProvider + else if (!m_xModel.is()) + { + throw RuntimeException( "PackageMasterScriptProvider is unitialised" ); + } + + } + else + { + if ( aName.isEmpty() ) + { + throw lang::IllegalArgumentException( "Name not set!!", + Reference < XInterface > (), 1 ); + } + // TODO for Script library package url parse the language, + // for the moment will just try to get each provider to see if the + // package exists in any provider, first one that succeed will + // terminate the loop + result = FindProviderAndApply( + *providerCache(), [&aName](Reference<container::XNameContainer>& xCont) { + return xCont->hasByName(aName); + }); + } + return result; +} + + +Sequence< OUString > SAL_CALL +MasterScriptProvider::getElementNames( ) +{ + // TODO needs implementing + throw RuntimeException( "getElementNames not implemented!!!!" ); +} + +Type SAL_CALL +MasterScriptProvider::getElementType( ) +{ + // TODO needs implementing + Type t; + return t; +} + +sal_Bool SAL_CALL MasterScriptProvider::hasElements( ) +{ + // TODO needs implementing + throw RuntimeException( "hasElements not implemented!!!!" ); +} + + +OUString SAL_CALL MasterScriptProvider::getImplementationName( ) +{ + return "com.sun.star.script.provider.MasterScriptProvider"; +} + +sal_Bool SAL_CALL MasterScriptProvider::supportsService( const OUString& serviceName ) +{ + return cppu::supportsService(this, serviceName); +} + + +Sequence< OUString > SAL_CALL MasterScriptProvider::getSupportedServiceNames( ) +{ + return { + "com.sun.star.script.provider.MasterScriptProvider", + "com.sun.star.script.browse.BrowseNode", + "com.sun.star.script.provider.ScriptProvider" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +scripting_MasterScriptProvider_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new MasterScriptProvider(context)); +} + +} // namespace func_provider + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |