1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <comphelper/SetFlagContextHelper.hxx>
22 #include <comphelper/documentinfo.hxx>
24 #include <cppuhelper/exc_hlp.hxx>
25 #include <cppuhelper/supportsservice.hxx>
26 #include <comphelper/diagnose_ex.hxx>
27 #include <tools/urlobj.hxx>
29 #include <com/sun/star/frame/XModel.hpp>
30 #include <com/sun/star/script/provider/ScriptFrameworkErrorException.hpp>
31 #include <com/sun/star/uri/XUriReference.hpp>
32 #include <com/sun/star/uri/UriReferenceFactory.hpp>
33 #include <com/sun/star/uri/XVndSunStarScriptUrl.hpp>
35 #include <com/sun/star/deployment/XPackage.hpp>
36 #include <com/sun/star/script/browse/BrowseNodeTypes.hpp>
37 #include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp>
38 #include <com/sun/star/script/provider/ScriptFrameworkErrorType.hpp>
40 #include <util/MiscUtils.hxx>
41 #include <sal/log.hxx>
43 #include "MasterScriptProvider.hxx"
45 using namespace ::com::sun::star
;
46 using namespace ::com::sun::star::uno
;
47 using namespace ::com::sun::star::script
;
48 using namespace ::com::sun::star::document
;
49 using namespace ::sf_misc
;
51 namespace func_provider
54 static bool endsWith( std::u16string_view target
, std::u16string_view item
)
56 size_t index
= target
.find( item
);
57 return index
!= std::u16string_view::npos
&&
58 index
== ( target
.size() - item
.size() );
61 /* should be available in some central location. */
63 // XScriptProvider implementation
66 MasterScriptProvider::MasterScriptProvider( const Reference
< XComponentContext
> & xContext
):
67 m_xContext( xContext
), m_bIsValid( false ), m_bInitialised( false ),
70 ENSURE_OR_THROW( m_xContext
.is(), "MasterScriptProvider::MasterScriptProvider: No context available\n" );
71 m_xMgr
= m_xContext
->getServiceManager();
72 ENSURE_OR_THROW( m_xMgr
.is(), "MasterScriptProvider::MasterScriptProvider: No service manager available\n" );
77 MasterScriptProvider::~MasterScriptProvider()
82 void SAL_CALL
MasterScriptProvider::initialize( const Sequence
< Any
>& args
)
89 sal_Int32 len
= args
.getLength();
92 throw RuntimeException(
93 u
"MasterScriptProvider::initialize: invalid number of arguments"_ustr
);
96 Sequence
< Any
> invokeArgs( len
);
100 auto pinvokeArgs
= invokeArgs
.getArray();
101 // check if first parameter is a string
102 // if it is, this implies that this is a MSP created
103 // with a user or share ctx ( used for browse functionality )
105 if ( args
[ 0 ] >>= m_sCtxString
)
107 pinvokeArgs
[ 0 ] = args
[ 0 ];
108 if ( m_sCtxString
.startsWith( "vnd.sun.star.tdoc" ) )
110 m_xModel
= MiscUtils::tDocUrlToModel( m_sCtxString
);
113 else if ( args
[ 0 ] >>= m_xInvocationContext
)
115 m_xModel
.set( m_xInvocationContext
->getScriptContainer(), UNO_QUERY_THROW
);
119 args
[ 0 ] >>= m_xModel
;
124 // from the arguments, we were able to deduce a model. That alone doesn't
125 // suffice, we also need an XEmbeddedScripts which actually indicates support
126 // for embedding scripts
127 Reference
< XEmbeddedScripts
> xScripts( m_xModel
, UNO_QUERY
);
128 if ( !xScripts
.is() )
130 throw lang::IllegalArgumentException(
131 u
"The given document does not support embedding scripts into it, and cannot be associated with such a document."_ustr
,
139 m_sCtxString
= MiscUtils::xModelToTdocUrl( m_xModel
, m_xContext
);
141 catch ( const Exception
& )
143 Any
aError( ::cppu::getCaughtException() );
145 Exception aException
;
146 aError
>>= aException
;
148 "MasterScriptProvider::initialize: caught " +
149 aError
.getValueTypeName() +
152 throw lang::WrappedTargetException( buf
, *this, aError
);
155 if ( m_xInvocationContext
.is() && m_xInvocationContext
!= m_xModel
)
156 pinvokeArgs
[ 0 ] <<= m_xInvocationContext
;
158 pinvokeArgs
[ 0 ] <<= m_sCtxString
;
161 OUString pkgSpec
= u
"uno_packages"_ustr
;
162 sal_Int32 indexOfPkgSpec
= m_sCtxString
.lastIndexOf( pkgSpec
);
164 // if context string ends with "uno_packages"
165 if ( indexOfPkgSpec
> -1 && m_sCtxString
.match( pkgSpec
, indexOfPkgSpec
) )
176 // use either scripting context or maybe zero args?
177 invokeArgs
= Sequence
< Any
>( 0 ); // no arguments
179 m_sAargs
= std::move(invokeArgs
);
180 // don't create pkg mgr MSP for documents, not supported
181 if ( !m_bIsPkgMSP
&& !m_xModel
.is() )
186 m_bInitialised
= true;
191 void MasterScriptProvider::createPkgProvider()
196 location
<<= m_sCtxString
+ ":uno_packages";
198 Reference
< provider::XScriptProviderFactory
> xFac
=
199 provider::theMasterScriptProviderFactory::get( m_xContext
);
202 xFac
->createScriptProvider( location
), UNO_SET_THROW
);
205 catch ( const Exception
& )
207 TOOLS_WARN_EXCEPTION("scripting.provider", "Exception creating MasterScriptProvider for uno_packages in context "
213 Reference
< provider::XScript
>
214 MasterScriptProvider::getScript( const OUString
& scriptURI
)
218 throw provider::ScriptFrameworkErrorException(
219 u
"MasterScriptProvider not initialised"_ustr
, Reference
< XInterface
>(),
221 provider::ScriptFrameworkErrorType::UNKNOWN
);
224 // need to get the language from the string
226 Reference
< uri::XUriReferenceFactory
> xFac ( uri::UriReferenceFactory::create( m_xContext
) );
228 Reference
< uri::XUriReference
> uriRef
= xFac
->parse( scriptURI
);
230 Reference
< uri::XVndSunStarScriptUrl
> sfUri( uriRef
, UNO_QUERY
);
232 if ( !uriRef
.is() || !sfUri
.is() )
234 throw provider::ScriptFrameworkErrorException(
235 "Incorrect format for Script URI: " + scriptURI
,
236 Reference
< XInterface
>(),
238 provider::ScriptFrameworkErrorType::UNKNOWN
);
241 OUString
langKey(u
"language"_ustr
);
242 OUString
locKey(u
"location"_ustr
);
244 if ( !sfUri
->hasParameter( langKey
) ||
245 !sfUri
->hasParameter( locKey
) ||
246 ( sfUri
->getName().isEmpty() ) )
248 throw provider::ScriptFrameworkErrorException(
249 "Incorrect format for Script URI: " + scriptURI
,
250 Reference
< XInterface
>(),
252 provider::ScriptFrameworkErrorType::UNKNOWN
);
255 OUString language
= sfUri
->getParameter( langKey
);
256 OUString location
= sfUri
->getParameter( locKey
);
258 // if script us located in uno pkg
259 sal_Int32 index
= -1;
260 OUString
pkgTag(u
":uno_packages"_ustr
);
261 // for languages other than basic, scripts located in uno packages
262 // are merged into the user/share location context.
263 // For other languages the location attribute in script url has the form
264 // location = [user|share]:uno_packages or location :uno_packages/xxxx.uno.pkg
265 // we need to extract the value of location part from the
266 // location attribute of the script, if the script is located in an
267 // uno package then that is the location part up to and including
268 // ":uno_packages", if the script is not in a uno package then the
269 // normal value is used e.g. user or share.
270 // The value extracted will be used to determine if the script is
271 // located in the same location context as this MSP.
272 // For Basic, the language script provider can handle the execution of a
273 // script in any location context
274 if ( ( index
= location
.indexOf( pkgTag
) ) > -1 )
276 location
= location
.copy( 0, index
+ pkgTag
.getLength() );
279 Reference
< provider::XScript
> xScript
;
281 // If the script location is in the same location context as this
282 // MSP then delete to the language provider controlled by this MSP
283 // ** Special case is BASIC, all calls to getScript will be handled
284 // by the language script provider in the current location context
285 // even if it's different
286 if ( ( location
== "document"
289 || ( endsWith( m_sCtxString
, location
) )
290 || ( language
== "Basic" )
293 Reference
< provider::XScriptProvider
> xScriptProvider
;
294 OUString serviceName
= "com.sun.star.script.provider.ScriptProviderFor" + language
;
295 if ( !providerCache() )
297 throw provider::ScriptFrameworkErrorException(
298 u
"No LanguageProviders detected"_ustr
,
299 Reference
< XInterface
>(),
300 sfUri
->getName(), language
,
301 provider::ScriptFrameworkErrorType::NOTSUPPORTED
);
307 providerCache()->getProvider( serviceName
),
310 catch( const Exception
& e
)
312 throw provider::ScriptFrameworkErrorException(
313 e
.Message
, Reference
< XInterface
>(),
314 sfUri
->getName(), language
,
315 provider::ScriptFrameworkErrorType::NOTSUPPORTED
);
318 xScript
=xScriptProvider
->getScript( scriptURI
);
322 Reference
< provider::XScriptProviderFactory
> xFac_
=
323 provider::theMasterScriptProviderFactory::get( m_xContext
);
325 Reference
< provider::XScriptProvider
> xSP(
326 xFac_
->createScriptProvider( Any( location
) ), UNO_SET_THROW
);
327 xScript
= xSP
->getScript( scriptURI
);
335 MasterScriptProvider::providerCache()
337 std::scoped_lock
aGuard( m_mutex
);
340 Sequence
<OUString
> denylist
{ u
"com.sun.star.script.provider.ScriptProviderForBasic"_ustr
};
344 m_pPCache
.reset( new ProviderCache( m_xContext
, m_sAargs
) );
348 m_pPCache
.reset( new ProviderCache( m_xContext
, m_sAargs
, denylist
) );
351 return m_pPCache
.get();
356 MasterScriptProvider::getName()
360 OUString sCtx
= getContextString();
361 if ( sCtx
.startsWith( "vnd.sun.star.tdoc" ) )
363 Reference
< frame::XModel
> xModel
= m_xModel
;
366 xModel
= MiscUtils::tDocUrlToModel( sCtx
);
369 m_sNodeName
= ::comphelper::DocumentInfo::getDocumentTitle( xModel
);
373 m_sNodeName
= parseLocationName( getContextString() );
378 m_sNodeName
= "uno_packages";
384 Sequence
< Reference
< browse::XBrowseNode
> > SAL_CALL
385 MasterScriptProvider::getChildNodes()
387 Sequence
< Reference
< provider::XScriptProvider
> > providers
= providerCache()->getAllProviders();
389 sal_Int32 size
= providers
.getLength();
390 bool hasPkgs
= m_xMSPPkg
.is();
395 Sequence
< Reference
< browse::XBrowseNode
> > children( size
);
396 auto childrenRange
= asNonConstRange(children
);
397 sal_Int32 provIndex
= 0;
398 for ( ; provIndex
< providers
.getLength(); provIndex
++ )
400 childrenRange
[ provIndex
].set( providers
[ provIndex
], UNO_QUERY
);
405 childrenRange
[ provIndex
].set( m_xMSPPkg
, UNO_QUERY
);
414 MasterScriptProvider::hasChildNodes()
421 MasterScriptProvider::getType()
423 return browse::BrowseNodeTypes::CONTAINER
;
428 MasterScriptProvider::parseLocationName( const OUString
& location
)
430 // strip out the last leaf of location name
431 // e.g. file://dir1/dir2/Blah.sxw - > Blah.sxw
432 OUString temp
= location
;
433 INetURLObject
aURLObj( temp
);
434 if ( !aURLObj
.HasError() )
435 temp
= aURLObj
.getName( INetURLObject::LAST_SEGMENT
, true, INetURLObject::DecodeMechanism::WithCharset
);
441 template <typename Proc
> bool FindProviderAndApply(ProviderCache
& rCache
, Proc p
)
443 auto pass
= [&rCache
, &p
]() -> bool
445 bool bResult
= false;
446 const Sequence
<Reference
<provider::XScriptProvider
>> aAllProviders
= rCache
.getAllProviders();
447 for (const auto& rProv
: aAllProviders
)
449 Reference
<container::XNameContainer
> xCont(rProv
, UNO_QUERY
);
460 catch (const Exception
&)
462 TOOLS_INFO_EXCEPTION("scripting.provider", "ignoring");
467 bool bSuccess
= false;
468 // 1. Try to perform the operation without trying to enable JVM (if disabled)
469 // This allows us to avoid useless user interaction in case when other provider
470 // (not JVM) actually handles the operation.
472 css::uno::ContextLayer
layer(comphelper::NoEnableJavaInteractionContext());
475 // 2. Now retry asking to enable JVM in case we didn't succeed first time
486 MasterScriptProvider::insertByName( const OUString
& aName
, const Any
& aElement
)
490 if ( !m_xMSPPkg
.is() )
492 throw RuntimeException( u
"PackageMasterScriptProvider is uninitialised"_ustr
);
495 Reference
< container::XNameContainer
> xCont( m_xMSPPkg
, UNO_QUERY_THROW
);
496 xCont
->insertByName( aName
, aElement
);
500 Reference
< deployment::XPackage
> xPkg( aElement
, UNO_QUERY
);
503 throw lang::IllegalArgumentException( u
"Couldn't convert to XPackage"_ustr
,
504 Reference
< XInterface
> (), 2 );
506 if ( aName
.isEmpty() )
508 throw lang::IllegalArgumentException( u
"Name not set!!"_ustr
,
509 Reference
< XInterface
> (), 1 );
511 // TODO for library package parse the language, for the moment will try
512 // to get each provider to process the new Package, the first one the succeeds
513 // will terminate processing
514 const bool bSuccess
= FindProviderAndApply(
515 *providerCache(), [&aName
, &aElement
](Reference
<container::XNameContainer
>& xCont
) {
516 xCont
->insertByName(aName
, aElement
);
521 // No script providers could process the package
522 throw lang::IllegalArgumentException( "Failed to register package for " + aName
,
523 Reference
< XInterface
> (), 2 );
531 MasterScriptProvider::removeByName( const OUString
& Name
)
535 if ( !m_xMSPPkg
.is() )
537 throw RuntimeException( u
"PackageMasterScriptProvider is uninitialised"_ustr
);
540 Reference
< container::XNameContainer
> xCont( m_xMSPPkg
, UNO_QUERY_THROW
);
541 xCont
->removeByName( Name
);
545 if ( Name
.isEmpty() )
547 throw lang::IllegalArgumentException( u
"Name not set!!"_ustr
,
548 Reference
< XInterface
> (), 1 );
550 // TODO for Script library package url parse the language,
551 // for the moment will just try to get each provider to process remove/revoke
552 // request, the first one the succeeds will terminate processing
553 const bool bSuccess
= FindProviderAndApply(
554 *providerCache(), [&Name
](Reference
<container::XNameContainer
>& xCont
) {
555 xCont
->removeByName(Name
);
560 // No script providers could process the package
561 throw lang::IllegalArgumentException( "Failed to revoke package for " + Name
,
562 Reference
< XInterface
> (), 1 );
570 MasterScriptProvider::replaceByName( const OUString
& /*aName*/, const Any
& /*aElement*/ )
572 // TODO needs implementing
573 throw RuntimeException( u
"replaceByName not implemented!!!!"_ustr
);
577 MasterScriptProvider::getByName( const OUString
& /*aName*/ )
579 // TODO needs to be implemented
580 throw RuntimeException( u
"getByName not implemented!!!!"_ustr
);
584 MasterScriptProvider::hasByName( const OUString
& aName
)
589 if ( m_xMSPPkg
.is() )
591 Reference
< container::XNameContainer
> xCont( m_xMSPPkg
, UNO_QUERY_THROW
);
592 result
= xCont
->hasByName( aName
);
594 // If this is a document provider then we shouldn't
595 // have a PackageProvider
596 else if (!m_xModel
.is())
598 throw RuntimeException( u
"PackageMasterScriptProvider is uninitialised"_ustr
);
604 if ( aName
.isEmpty() )
606 throw lang::IllegalArgumentException( u
"Name not set!!"_ustr
,
607 Reference
< XInterface
> (), 1 );
609 // TODO for Script library package url parse the language,
610 // for the moment will just try to get each provider to see if the
611 // package exists in any provider, first one that succeed will
612 // terminate the loop
613 result
= FindProviderAndApply(
614 *providerCache(), [&aName
](Reference
<container::XNameContainer
>& xCont
) {
615 return xCont
->hasByName(aName
);
622 Sequence
< OUString
> SAL_CALL
623 MasterScriptProvider::getElementNames( )
625 // TODO needs implementing
626 throw RuntimeException( u
"getElementNames not implemented!!!!"_ustr
);
630 MasterScriptProvider::getElementType( )
632 // TODO needs implementing
637 sal_Bool SAL_CALL
MasterScriptProvider::hasElements( )
639 // TODO needs implementing
640 throw RuntimeException( u
"hasElements not implemented!!!!"_ustr
);
644 OUString SAL_CALL
MasterScriptProvider::getImplementationName( )
646 return u
"com.sun.star.script.provider.MasterScriptProvider"_ustr
;
649 sal_Bool SAL_CALL
MasterScriptProvider::supportsService( const OUString
& serviceName
)
651 return cppu::supportsService(this, serviceName
);
655 Sequence
< OUString
> SAL_CALL
MasterScriptProvider::getSupportedServiceNames( )
658 u
"com.sun.star.script.provider.MasterScriptProvider"_ustr
,
659 u
"com.sun.star.script.browse.BrowseNode"_ustr
,
660 u
"com.sun.star.script.provider.ScriptProvider"_ustr
};
663 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
664 scripting_MasterScriptProvider_get_implementation(
665 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const&)
667 return cppu::acquire(new MasterScriptProvider(context
));
670 } // namespace func_provider
673 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */