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 .
20 #include "scripthandler.hxx"
22 #include <com/sun/star/frame/DispatchResultEvent.hpp>
23 #include <com/sun/star/frame/DispatchResultState.hpp>
24 #include <com/sun/star/frame/XController.hpp>
25 #include <com/sun/star/frame/XModel.hpp>
27 #include <com/sun/star/document/XEmbeddedScripts.hpp>
28 #include <com/sun/star/document/XScriptInvocationContext.hpp>
30 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
31 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
32 #include <com/sun/star/script/provider/ScriptFrameworkErrorException.hpp>
33 #include <com/sun/star/script/provider/XScriptProviderSupplier.hpp>
34 #include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp>
35 #include <com/sun/star/script/provider/ScriptFrameworkErrorType.hpp>
37 #include <sfx2/objsh.hxx>
38 #include <sfx2/frame.hxx>
39 #include <sfx2/sfxdlg.hxx>
40 #include <comphelper/diagnose_ex.hxx>
42 #include <cppuhelper/exc_hlp.hxx>
43 #include <cppuhelper/supportsservice.hxx>
44 #include <framework/documentundoguard.hxx>
45 #include <officecfg/Office/Common.hxx>
46 #include <unotools/securityoptions.hxx>
48 #include <com/sun/star/uri/XUriReference.hpp>
49 #include <com/sun/star/uri/XVndSunStarScriptUrlReference.hpp>
50 #include <com/sun/star/uri/UriReferenceFactory.hpp>
54 using namespace ::com::sun::star
;
55 using namespace ::com::sun::star::uno
;
56 using namespace ::com::sun::star::frame
;
57 using namespace ::com::sun::star::util
;
58 using namespace ::com::sun::star::beans
;
59 using namespace ::com::sun::star::lang
;
60 using namespace ::com::sun::star::script
;
61 using namespace ::com::sun::star::script::provider
;
62 using namespace ::com::sun::star::document
;
64 namespace scripting_protocolhandler
67 void SAL_CALL
ScriptProtocolHandler::initialize(
68 const css::uno::Sequence
< css::uno::Any
>& aArguments
)
75 // first argument contains a reference to the frame (may be empty or the desktop,
76 // but usually it's a "real" frame)
77 if ( aArguments
.hasElements() && !( aArguments
[ 0 ] >>= m_xFrame
) )
79 throw RuntimeException( u
"ScriptProtocolHandler::initialize: could not extract reference to the frame"_ustr
);
82 ENSURE_OR_THROW( m_xContext
.is(), "ScriptProtocolHandler::initialize: No Service Manager available" );
83 m_bInitialised
= true;
86 Reference
< XDispatch
> SAL_CALL
ScriptProtocolHandler::queryDispatch(
87 const URL
& aURL
, const OUString
&, sal_Int32
)
89 Reference
< XDispatch
> xDispatcher
;
92 Reference
< uri::XUriReferenceFactory
> xFac
= uri::UriReferenceFactory::create( m_xContext
);
93 Reference
< uri::XUriReference
> uriRef
= xFac
->parse( aURL
.Complete
);
96 if ( uriRef
->getScheme() == "vnd.sun.star.script" )
105 Sequence
< Reference
< XDispatch
> > SAL_CALL
106 ScriptProtocolHandler::queryDispatches(
107 const Sequence
< DispatchDescriptor
>& seqDescriptor
)
109 sal_Int32 nCount
= seqDescriptor
.getLength();
110 Sequence
< Reference
< XDispatch
> > lDispatcher( nCount
);
111 std::transform(seqDescriptor
.begin(), seqDescriptor
.end(), lDispatcher
.getArray(),
112 [this](const DispatchDescriptor
& rDescr
) -> Reference
<XDispatch
> {
113 return queryDispatch(rDescr
.FeatureURL
, rDescr
.FrameName
, rDescr
.SearchFlags
); });
117 void SAL_CALL
ScriptProtocolHandler::dispatchWithNotification(
118 const URL
& aURL
, const Sequence
< PropertyValue
>& lArgs
,
119 const Reference
< XDispatchResultListener
>& xListener
)
121 if (SvtSecurityOptions::IsMacroDisabled())
124 sal_Int16 aState
= css::frame::DispatchResultState::FAILURE
;
126 bool bCaughtException
= false;
129 if ( m_bInitialised
)
133 css::uno::Reference
<css::uri::XUriReferenceFactory
> urifac(
134 css::uri::UriReferenceFactory::create(m_xContext
));
135 css::uno::Reference
<css::uri::XVndSunStarScriptUrlReference
> uri(
136 urifac
->parse(aURL
.Complete
), css::uno::UNO_QUERY_THROW
);
137 auto const loc
= uri
->getParameter(u
"location"_ustr
);
138 bool bIsDocumentScript
= loc
== "document";
140 if ( bIsDocumentScript
)
142 // obtain the component for our security check
143 Reference
< XEmbeddedScripts
> xDocumentScripts
;
144 if ( getScriptInvocation() )
145 xDocumentScripts
.set( m_xScriptInvocation
->getScriptContainer(), UNO_SET_THROW
);
147 OSL_ENSURE( xDocumentScripts
.is(), "ScriptProtocolHandler::dispatchWithNotification: can't do the security check!" );
148 if ( !xDocumentScripts
.is() || !xDocumentScripts
->getAllowMacroExecution() )
150 if ( xListener
.is() )
152 css::frame::DispatchResultEvent
aEvent(
154 css::frame::DispatchResultState::FAILURE
,
158 xListener
->dispatchFinished( aEvent
) ;
160 catch(const RuntimeException
&)
162 TOOLS_WARN_EXCEPTION("scripting",
163 "ScriptProtocolHandler::dispatchWithNotification: caught RuntimeException"
164 "while dispatchFinished with failure of the execution");
171 // Creates a ScriptProvider ( if one is not created already )
172 createScriptProvider();
174 Reference
< provider::XScript
> xFunc
=
175 m_xScriptProvider
->getScript( aURL
.Complete
);
176 ENSURE_OR_THROW( xFunc
.is(),
177 "ScriptProtocolHandler::dispatchWithNotification: validate xFunc - unable to obtain XScript interface" );
180 Sequence
< Any
> inArgs
;
181 Sequence
< Any
> outArgs
;
182 Sequence
< sal_Int16
> outIndex
;
184 if ( lArgs
.hasElements() )
187 for ( const auto& rArg
: lArgs
)
189 // Sometimes we get a propertyval with name = "Referer" or "SynchronMode". These
190 // are not actual arguments to be passed to script, but flags describing the
191 // call, so ignore. Who thought that passing such "meta-arguments" mixed in with
192 // real arguments was a good idea?
193 if ( (rArg
.Name
!= "Referer" &&
194 rArg
.Name
!= "SynchronMode") ||
195 rArg
.Name
.isEmpty() ) //TODO:???
197 inArgs
.realloc( ++argCount
);
198 inArgs
.getArray()[ argCount
- 1 ] = rArg
.Value
;
203 // attempt to protect the document against the script tampering with its Undo Context
204 std::unique_ptr
< ::framework::DocumentUndoGuard
> pUndoGuard
;
205 if ( bIsDocumentScript
)
206 pUndoGuard
.reset( new ::framework::DocumentUndoGuard( m_xScriptInvocation
) );
210 std::exception_ptr aFirstCaughtException
;
213 invokeResult
= xFunc
->invoke( inArgs
, outIndex
, outArgs
);
214 aState
= css::frame::DispatchResultState::SUCCESS
;
217 catch( const provider::ScriptFrameworkErrorException
& se
)
219 if (!aFirstCaughtException
)
220 aFirstCaughtException
= std::current_exception();
222 if ( se
.errorType
!= provider::ScriptFrameworkErrorType::NO_SUCH_SCRIPT
)
223 // the only condition which allows us to retry is if there is no method with the
224 // given name/signature
225 std::rethrow_exception(aFirstCaughtException
);
227 if ( !inArgs
.hasElements() )
228 // no chance to retry if we can't strip more in-args
229 std::rethrow_exception(std::move(aFirstCaughtException
));
231 // strip one argument, then retry
232 inArgs
.realloc( inArgs
.getLength() - 1 );
236 // Office doesn't handle exceptions rethrown here very well, it cores,
237 // all we can is log them and then set fail for the dispatch event!
238 // (if there is a listener of course)
239 catch ( const Exception
& e
)
241 aException
= ::cppu::getCaughtException();
243 invokeResult
<<= "ScriptProtocolHandler::dispatch: caught "
244 + aException
.getValueTypeName() + ": " + e
.Message
;
246 bCaughtException
= true;
251 invokeResult
<<= u
"ScriptProtocolHandler::dispatchWithNotification failed, ScriptProtocolHandler not initialised"_ustr
;
254 if ( bCaughtException
)
256 SfxAbstractDialogFactory
* pFact
= SfxAbstractDialogFactory::Create();
257 pFact
->ShowAsyncScriptErrorDialog( nullptr, aException
);
260 if ( !xListener
.is() )
263 // always call dispatchFinished(), because we didn't load a document but
264 // executed a macro instead!
265 css::frame::DispatchResultEvent aEvent
;
267 aEvent
.Source
= getXWeak();
268 aEvent
.Result
= std::move(invokeResult
);
269 aEvent
.State
= aState
;
273 xListener
->dispatchFinished( aEvent
) ;
275 catch(const RuntimeException
&)
277 TOOLS_WARN_EXCEPTION("scripting",
278 "ScriptProtocolHandler::dispatchWithNotification: caught RuntimeException"
279 "while dispatchFinished" );
283 void SAL_CALL
ScriptProtocolHandler::dispatch(
284 const URL
& aURL
, const Sequence
< PropertyValue
>& lArgs
)
286 dispatchWithNotification( aURL
, lArgs
, Reference
< XDispatchResultListener
>() );
289 void SAL_CALL
ScriptProtocolHandler::addStatusListener(
290 const Reference
< XStatusListener
>&, const URL
& )
292 // implement if status is supported
295 void SAL_CALL
ScriptProtocolHandler::removeStatusListener(
296 const Reference
< XStatusListener
>&, const URL
& )
300 ScriptProtocolHandler::getScriptInvocation()
302 if ( !m_xScriptInvocation
.is() && m_xFrame
.is() )
304 Reference
< XController
> xController
= m_xFrame
->getController();
305 if ( xController
.is() )
307 // try to obtain an XScriptInvocationContext interface, preferred from the
308 // mode, then from the controller
309 if ( !m_xScriptInvocation
.set( xController
->getModel(), UNO_QUERY
) )
310 m_xScriptInvocation
.set( xController
, UNO_QUERY
);
316 SfxFrame
* pFrame
= nullptr;
317 for ( pFrame
= SfxFrame::GetFirst(); pFrame
; pFrame
= SfxFrame::GetNext( *pFrame
) )
319 if ( pFrame
->GetFrameInterface() == m_xFrame
)
322 if (SfxObjectShell
* pDocShell
= pFrame
? pFrame
->GetCurrentDocument() : SfxObjectShell::Current())
324 Reference
< XModel
> xModel( pDocShell
->GetModel() );
325 m_xScriptInvocation
.set( xModel
, UNO_QUERY
);
330 return m_xScriptInvocation
.is();
333 void ScriptProtocolHandler::createScriptProvider()
335 if ( m_xScriptProvider
.is() )
340 // first, ask the component supporting the XScriptInvocationContext interface
341 // (if there is one) for a script provider
342 if ( getScriptInvocation() )
344 Reference
< XScriptProviderSupplier
> xSPS( m_xScriptInvocation
, UNO_QUERY
);
346 m_xScriptProvider
= xSPS
->getScriptProvider();
349 // second, ask the model in our frame
350 if ( !m_xScriptProvider
.is() && m_xFrame
.is() )
352 Reference
< XController
> xController
= m_xFrame
->getController();
353 if ( xController
.is() )
355 Reference
< XScriptProviderSupplier
> xSPS( xController
->getModel(), UNO_QUERY
);
357 m_xScriptProvider
= xSPS
->getScriptProvider();
362 // as a fallback, ask the controller
363 if ( !m_xScriptProvider
.is() && m_xFrame
.is() )
365 Reference
< XScriptProviderSupplier
> xSPS( m_xFrame
->getController(), UNO_QUERY
);
367 m_xScriptProvider
= xSPS
->getScriptProvider();
370 // if nothing of this is successful, use the master script provider
371 if ( !m_xScriptProvider
.is() )
373 Reference
< provider::XScriptProviderFactory
> xFac
=
374 provider::theMasterScriptProviderFactory::get( m_xContext
);
377 if ( getScriptInvocation() )
378 aContext
<<= m_xScriptInvocation
;
379 m_xScriptProvider
.set( xFac
->createScriptProvider( aContext
), UNO_SET_THROW
);
382 catch ( const Exception
& e
)
384 css::uno::Any anyEx
= cppu::getCaughtException();
385 throw css::lang::WrappedTargetRuntimeException(
386 "ScriptProtocolHandler::createScriptProvider: " + e
.Message
,
391 ScriptProtocolHandler::ScriptProtocolHandler( const Reference
< css::uno::XComponentContext
> & xContext
)
392 : m_bInitialised( false ), m_xContext( xContext
)
396 ScriptProtocolHandler::~ScriptProtocolHandler()
401 OUString SAL_CALL
ScriptProtocolHandler::getImplementationName( )
403 return u
"com.sun.star.comp.ScriptProtocolHandler"_ustr
;
407 sal_Bool SAL_CALL
ScriptProtocolHandler::supportsService(const OUString
& sServiceName
)
409 return cppu::supportsService(this, sServiceName
);
413 Sequence
< OUString
> SAL_CALL
ScriptProtocolHandler::getSupportedServiceNames()
415 return {u
"com.sun.star.frame.ProtocolHandler"_ustr
};
418 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
419 scripting_ScriptProtocolHandler_get_implementation(
420 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const&)
422 return cppu::acquire(new ScriptProtocolHandler(context
));
425 } // namespace scripting_protocolhandler
428 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */