LibreOffice Module scripting (master) 1
MasterScriptProvider.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20
23
27#include <tools/urlobj.hxx>
28
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>
34
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>
39
40#include <util/MiscUtils.hxx>
41#include <sal/log.hxx>
42
44
45using namespace ::com::sun::star;
46using namespace ::com::sun::star::uno;
47using namespace ::com::sun::star::script;
48using namespace ::com::sun::star::document;
49using namespace ::sf_misc;
50
51namespace func_provider
52{
53
54static bool endsWith( std::u16string_view target, std::u16string_view item )
55{
56 size_t index = target.find( item );
57 return index != std::u16string_view::npos &&
58 index == ( target.size() - item.size() );
59}
60
61/* should be available in some central location. */
62
63// XScriptProvider implementation
64
65
66MasterScriptProvider::MasterScriptProvider( const Reference< XComponentContext > & xContext ):
67 m_xContext( xContext ), m_bIsValid( false ), m_bInitialised( false ),
68 m_bIsPkgMSP( false )
69{
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" );
73 m_bIsValid = true;
74}
75
76
78{
79}
80
81
82void SAL_CALL MasterScriptProvider::initialize( const Sequence < Any >& args )
83{
84 if ( m_bInitialised )
85 return;
86
87 m_bIsValid = false;
88
89 sal_Int32 len = args.getLength();
90 if ( len > 1 )
91 {
92 throw RuntimeException(
93 "MasterScriptProvider::initialize: invalid number of arguments" );
94 }
95
96 Sequence< Any > invokeArgs( len );
97
98 if ( len != 0 )
99 {
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 )
104
105 if ( args[ 0 ] >>= m_sCtxString )
106 {
107 pinvokeArgs[ 0 ] = args[ 0 ];
108 if ( m_sCtxString.startsWith( "vnd.sun.star.tdoc" ) )
109 {
110 m_xModel = MiscUtils::tDocUrlToModel( m_sCtxString );
111 }
112 }
113 else if ( args[ 0 ] >>= m_xInvocationContext )
114 {
115 m_xModel.set( m_xInvocationContext->getScriptContainer(), UNO_QUERY_THROW );
116 }
117 else
118 {
119 args[ 0 ] >>= m_xModel;
120 }
121
122 if ( m_xModel.is() )
123 {
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() )
129 {
130 throw lang::IllegalArgumentException(
131 "The given document does not support embedding scripts into it, and cannot be associated with such a document.",
132 *this,
133 1
134 );
135 }
136
137 try
138 {
139 m_sCtxString = MiscUtils::xModelToTdocUrl( m_xModel, m_xContext );
140 }
141 catch ( const Exception& )
142 {
143 Any aError( ::cppu::getCaughtException() );
144
145 Exception aException;
146 aError >>= aException;
147 OUString buf =
148 "MasterScriptProvider::initialize: caught " +
149 aError.getValueTypeName() +
150 ":" +
151 aException.Message;
152 throw lang::WrappedTargetException( buf, *this, aError );
153 }
154
156 pinvokeArgs[ 0 ] <<= m_xInvocationContext;
157 else
158 pinvokeArgs[ 0 ] <<= m_sCtxString;
159 }
160
161 OUString pkgSpec = "uno_packages";
162 sal_Int32 indexOfPkgSpec = m_sCtxString.lastIndexOf( pkgSpec );
163
164 // if context string ends with "uno_packages"
165 if ( indexOfPkgSpec > -1 && m_sCtxString.match( pkgSpec, indexOfPkgSpec ) )
166 {
167 m_bIsPkgMSP = true;
168 }
169 else
170 {
171 m_bIsPkgMSP = false;
172 }
173 }
174 else // no args
175 {
176 // use either scripting context or maybe zero args?
177 invokeArgs = Sequence< Any >( 0 ); // no arguments
178 }
179 m_sAargs = invokeArgs;
180 // don't create pkg mgr MSP for documents, not supported
181 if ( !m_bIsPkgMSP && !m_xModel.is() )
182 {
184 }
185
186 m_bInitialised = true;
187 m_bIsValid = true;
188}
189
190
192{
193 try
194 {
195 Any location;
196 location <<= m_sCtxString + ":uno_packages";
197
198 Reference< provider::XScriptProviderFactory > xFac =
199 provider::theMasterScriptProviderFactory::get( m_xContext );
200
201 m_xMSPPkg.set(
202 xFac->createScriptProvider( location ), UNO_SET_THROW );
203
204 }
205 catch ( const Exception& )
206 {
207 TOOLS_WARN_EXCEPTION("scripting.provider", "Exception creating MasterScriptProvider for uno_packages in context "
208 << m_sCtxString );
209 }
210}
211
212
213Reference< provider::XScript >
214MasterScriptProvider::getScript( const OUString& scriptURI )
215{
216 if ( !m_bIsValid )
217 {
218 throw provider::ScriptFrameworkErrorException(
219 "MasterScriptProvider not initialised", Reference< XInterface >(),
220 scriptURI, "",
221 provider::ScriptFrameworkErrorType::UNKNOWN );
222 }
223
224 // need to get the language from the string
225
226 Reference< uri::XUriReferenceFactory > xFac ( uri::UriReferenceFactory::create( m_xContext ) );
227
228 Reference< uri::XUriReference > uriRef = xFac->parse( scriptURI );
229
230 Reference < uri::XVndSunStarScriptUrl > sfUri( uriRef, UNO_QUERY );
231
232 if ( !uriRef.is() || !sfUri.is() )
233 {
234 throw provider::ScriptFrameworkErrorException(
235 "Incorrect format for Script URI: " + scriptURI,
236 Reference< XInterface >(),
237 scriptURI, "",
238 provider::ScriptFrameworkErrorType::UNKNOWN );
239 }
240
241 OUString langKey("language");
242 OUString locKey("location");
243
244 if ( !sfUri->hasParameter( langKey ) ||
245 !sfUri->hasParameter( locKey ) ||
246 ( sfUri->getName().isEmpty() ) )
247 {
248 throw provider::ScriptFrameworkErrorException(
249 "Incorrect format for Script URI: " + scriptURI,
250 Reference< XInterface >(),
251 scriptURI, "",
252 provider::ScriptFrameworkErrorType::UNKNOWN );
253 }
254
255 OUString language = sfUri->getParameter( langKey );
256 OUString location = sfUri->getParameter( locKey );
257
258 // if script us located in uno pkg
259 sal_Int32 index = -1;
260 OUString pkgTag(":uno_packages");
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 )
275 {
276 location = location.copy( 0, index + pkgTag.getLength() );
277 }
278
279 Reference< provider::XScript > xScript;
280
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"
287 && m_xModel.is()
288 )
289 || ( endsWith( m_sCtxString, location ) )
290 || ( language == "Basic" )
291 )
292 {
293 Reference< provider::XScriptProvider > xScriptProvider;
294 OUString serviceName = "com.sun.star.script.provider.ScriptProviderFor" + language;
295 if ( !providerCache() )
296 {
297 throw provider::ScriptFrameworkErrorException(
298 "No LanguageProviders detected",
299 Reference< XInterface >(),
300 sfUri->getName(), language,
301 provider::ScriptFrameworkErrorType::NOTSUPPORTED );
302 }
303
304 try
305 {
306 xScriptProvider.set(
307 providerCache()->getProvider( serviceName ),
308 UNO_SET_THROW );
309 }
310 catch( const Exception& e )
311 {
312 throw provider::ScriptFrameworkErrorException(
313 e.Message, Reference< XInterface >(),
314 sfUri->getName(), language,
315 provider::ScriptFrameworkErrorType::NOTSUPPORTED );
316 }
317
318 xScript=xScriptProvider->getScript( scriptURI );
319 }
320 else
321 {
322 Reference< provider::XScriptProviderFactory > xFac_ =
323 provider::theMasterScriptProviderFactory::get( m_xContext );
324
325 Reference< provider::XScriptProvider > xSP(
326 xFac_->createScriptProvider( Any( location ) ), UNO_SET_THROW );
327 xScript = xSP->getScript( scriptURI );
328 }
329
330 return xScript;
331}
332
333
336{
337 if ( !m_pPCache )
338 {
339 std::scoped_lock aGuard( m_mutex );
340 if ( !m_pPCache )
341 {
342 Sequence<OUString> denylist { "com.sun.star.script.provider.ScriptProviderForBasic" };
343
344 if ( !m_bIsPkgMSP )
345 {
347 }
348 else
349 {
350 m_pPCache.reset( new ProviderCache( m_xContext, m_sAargs, denylist ) );
351 }
352 }
353 }
354 return m_pPCache.get();
355}
356
357
358OUString SAL_CALL
360{
361 if ( !m_bIsPkgMSP )
362 {
363 OUString sCtx = getContextString();
364 if ( sCtx.startsWith( "vnd.sun.star.tdoc" ) )
365 {
367 if ( !xModel.is() )
368 {
369 xModel = MiscUtils::tDocUrlToModel( sCtx );
370 }
371
372 m_sNodeName = ::comphelper::DocumentInfo::getDocumentTitle( xModel );
373 }
374 else
375 {
377 }
378 }
379 else
380 {
381 m_sNodeName = "uno_packages";
382 }
383 return m_sNodeName;
384}
385
386
387Sequence< Reference< browse::XBrowseNode > > SAL_CALL
389{
390 Sequence< Reference< provider::XScriptProvider > > providers = providerCache()->getAllProviders();
391
392 sal_Int32 size = providers.getLength();
393 bool hasPkgs = m_xMSPPkg.is();
394 if ( hasPkgs )
395 {
396 size++;
397 }
398 Sequence< Reference< browse::XBrowseNode > > children( size );
399 auto childrenRange = asNonConstRange(children);
400 sal_Int32 provIndex = 0;
401 for ( ; provIndex < providers.getLength(); provIndex++ )
402 {
403 childrenRange[ provIndex ].set( providers[ provIndex ], UNO_QUERY );
404 }
405
406 if ( hasPkgs )
407 {
408 childrenRange[ provIndex ].set( m_xMSPPkg, UNO_QUERY );
409
410 }
411
412 return children;
413}
414
415
416sal_Bool SAL_CALL
418{
419 return true;
420}
421
422
423sal_Int16 SAL_CALL
425{
426 return browse::BrowseNodeTypes::CONTAINER;
427}
428
429
430OUString
432{
433 // strip out the last leaf of location name
434 // e.g. file://dir1/dir2/Blah.sxw - > Blah.sxw
435 OUString temp = location;
436 INetURLObject aURLObj( temp );
437 if ( !aURLObj.HasError() )
439 return temp;
440}
441
442namespace
443{
444template <typename Proc> bool FindProviderAndApply(ProviderCache& rCache, Proc p)
445{
446 auto pass = [&rCache, &p]() -> bool
447 {
448 bool bResult = false;
449 const Sequence<Reference<provider::XScriptProvider>> aAllProviders = rCache.getAllProviders();
450 for (const auto& rProv : aAllProviders)
451 {
452 Reference<container::XNameContainer> xCont(rProv, UNO_QUERY);
453 if (!xCont.is())
454 {
455 continue;
456 }
457 try
458 {
459 bResult = p(xCont);
460 if (bResult)
461 break;
462 }
463 catch (const Exception&)
464 {
465 TOOLS_INFO_EXCEPTION("scripting.provider", "ignoring");
466 }
467 }
468 return bResult;
469 };
470 bool bSuccess = false;
471 // 1. Try to perform the operation without trying to enable JVM (if disabled)
472 // This allows us to avoid useless user interaction in case when other provider
473 // (not JVM) actually handles the operation.
474 {
475 css::uno::ContextLayer layer(comphelper::NoEnableJavaInteractionContext());
476 bSuccess = pass();
477 }
478 // 2. Now retry asking to enable JVM in case we didn't succeed first time
479 if (!bSuccess)
480 {
481 bSuccess = pass();
482 }
483 return bSuccess;
484}
485} // namespace
486
487// Register Package
488void SAL_CALL
489MasterScriptProvider::insertByName( const OUString& aName, const Any& aElement )
490{
491 if ( !m_bIsPkgMSP )
492 {
493 if ( !m_xMSPPkg.is() )
494 {
495 throw RuntimeException( "PackageMasterScriptProvider is unitialised" );
496 }
497
498 Reference< container::XNameContainer > xCont( m_xMSPPkg, UNO_QUERY_THROW );
499 xCont->insertByName( aName, aElement );
500 }
501 else
502 {
503 Reference< deployment::XPackage > xPkg( aElement, UNO_QUERY );
504 if ( !xPkg.is() )
505 {
506 throw lang::IllegalArgumentException( "Couldn't convert to XPackage",
507 Reference < XInterface > (), 2 );
508 }
509 if ( aName.isEmpty() )
510 {
511 throw lang::IllegalArgumentException( "Name not set!!",
512 Reference < XInterface > (), 1 );
513 }
514 // TODO for library package parse the language, for the moment will try
515 // to get each provider to process the new Package, the first one the succeeds
516 // will terminate processing
517 const bool bSuccess = FindProviderAndApply(
518 *providerCache(), [&aName, &aElement](Reference<container::XNameContainer>& xCont) {
519 xCont->insertByName(aName, aElement);
520 return true;
521 });
522 if (!bSuccess)
523 {
524 // No script providers could process the package
525 throw lang::IllegalArgumentException( "Failed to register package for " + aName,
526 Reference < XInterface > (), 2 );
527 }
528 }
529}
530
531
532// Revoke Package
533void SAL_CALL
535{
536 if ( !m_bIsPkgMSP )
537 {
538 if ( !m_xMSPPkg.is() )
539 {
540 throw RuntimeException( "PackageMasterScriptProvider is unitialised" );
541 }
542
543 Reference< container::XNameContainer > xCont( m_xMSPPkg, UNO_QUERY_THROW );
544 xCont->removeByName( Name );
545 }
546 else
547 {
548 if ( Name.isEmpty() )
549 {
550 throw lang::IllegalArgumentException( "Name not set!!",
551 Reference < XInterface > (), 1 );
552 }
553 // TODO for Script library package url parse the language,
554 // for the moment will just try to get each provider to process remove/revoke
555 // request, the first one the succeeds will terminate processing
556 const bool bSuccess = FindProviderAndApply(
557 *providerCache(), [&Name](Reference<container::XNameContainer>& xCont) {
558 xCont->removeByName(Name);
559 return true;
560 });
561 if (!bSuccess)
562 {
563 // No script providers could process the package
564 throw lang::IllegalArgumentException( "Failed to revoke package for " + Name,
565 Reference < XInterface > (), 1 );
566 }
567
568 }
569}
570
571
572void SAL_CALL
573MasterScriptProvider::replaceByName( const OUString& /*aName*/, const Any& /*aElement*/ )
574{
575 // TODO needs implementing
576 throw RuntimeException( "replaceByName not implemented!!!!" );
577}
578
579Any SAL_CALL
580MasterScriptProvider::getByName( const OUString& /*aName*/ )
581{
582 // TODO needs to be implemented
583 throw RuntimeException( "getByName not implemented!!!!" );
584}
585
586sal_Bool SAL_CALL
587MasterScriptProvider::hasByName( const OUString& aName )
588{
589 bool result = false;
590 if ( !m_bIsPkgMSP )
591 {
592 if ( m_xMSPPkg.is() )
593 {
594 Reference< container::XNameContainer > xCont( m_xMSPPkg, UNO_QUERY_THROW );
595 result = xCont->hasByName( aName );
596 }
597 // If this is a document provider then we shouldn't
598 // have a PackageProvider
599 else if (!m_xModel.is())
600 {
601 throw RuntimeException( "PackageMasterScriptProvider is unitialised" );
602 }
603
604 }
605 else
606 {
607 if ( aName.isEmpty() )
608 {
609 throw lang::IllegalArgumentException( "Name not set!!",
610 Reference < XInterface > (), 1 );
611 }
612 // TODO for Script library package url parse the language,
613 // for the moment will just try to get each provider to see if the
614 // package exists in any provider, first one that succeed will
615 // terminate the loop
616 result = FindProviderAndApply(
617 *providerCache(), [&aName](Reference<container::XNameContainer>& xCont) {
618 return xCont->hasByName(aName);
619 });
620 }
621 return result;
622}
623
624
627{
628 // TODO needs implementing
629 throw RuntimeException( "getElementNames not implemented!!!!" );
630}
631
632Type SAL_CALL
634{
635 // TODO needs implementing
636 Type t;
637 return t;
638}
639
641{
642 // TODO needs implementing
643 throw RuntimeException( "hasElements not implemented!!!!" );
644}
645
646
648{
649 return "com.sun.star.script.provider.MasterScriptProvider";
650}
651
652sal_Bool SAL_CALL MasterScriptProvider::supportsService( const OUString& serviceName )
653{
654 return cppu::supportsService(this, serviceName);
655}
656
657
659{
660 return {
661 "com.sun.star.script.provider.MasterScriptProvider",
662 "com.sun.star.script.browse.BrowseNode",
663 "com.sun.star.script.provider.ScriptProvider" };
664}
665
666extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
668 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
669{
670 return cppu::acquire(new MasterScriptProvider(context));
671}
672
673} // namespace func_provider
674
675
676/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XComponentContext > m_xContext
XPropertyListType t
OUString getName(sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true, DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
bool HasError() const
virtual css::uno::Any SAL_CALL getByName(const OUString &aName) override
virtual sal_Bool SAL_CALL supportsService(const OUString &ServiceName) override
css::uno::Reference< css::uno::XComponentContext > m_xContext
virtual sal_Int16 SAL_CALL getType() override
css::uno::Sequence< css::uno::Any > m_sAargs
virtual OUString SAL_CALL getName() override
virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any > &args) override
XInitialise implementation.
css::uno::Reference< css::document::XScriptInvocationContext > m_xInvocationContext
virtual css::uno::Sequence< css::uno::Reference< css::script::browse::XBrowseNode > > SAL_CALL getChildNodes() override
virtual sal_Bool SAL_CALL hasElements() override
virtual void SAL_CALL insertByName(const OUString &aName, const css::uno::Any &aElement) override
css::uno::Reference< css::lang::XMultiComponentFactory > m_xMgr
virtual sal_Bool SAL_CALL hasByName(const OUString &aName) override
virtual void SAL_CALL removeByName(const OUString &Name) override
virtual css::uno::Type SAL_CALL getElementType() override
std::unique_ptr< ProviderCache > m_pPCache
virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override
MasterScriptProvider(const css::uno::Reference< css::uno::XComponentContext > &xContext)
virtual css::uno::Reference< css::script::provider::XScript > SAL_CALL getScript(const OUString &scriptURI) override
virtual OUString SAL_CALL getImplementationName() override
static OUString parseLocationName(const OUString &location)
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
virtual void SAL_CALL replaceByName(const OUString &aName, const css::uno::Any &aElement) override
css::uno::Reference< css::frame::XModel > m_xModel
virtual sal_Bool SAL_CALL hasChildNodes() override
css::uno::Reference< css::script::provider::XScriptProvider > m_xMSPPkg
css::uno::Sequence< css::uno::Reference< css::script::provider::XScriptProvider > > getAllProviders()
#define TOOLS_WARN_EXCEPTION(area, stream)
#define ENSURE_OR_THROW(c, m)
#define TOOLS_INFO_EXCEPTION(area, stream)
OUString aName
void * p
size
@ Exception
css::uno::Reference< css::uno::XCurrentContext > NoEnableJavaInteractionContext()
Type
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
static bool endsWith(std::u16string_view target, std::u16string_view item)
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * scripting_MasterScriptProvider_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
index
args
pass
Reference< XModel > xModel
OUString Name
unsigned char sal_Bool
Any result