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>
47 #include <com/sun/star/uri/XUriReference.hpp>
48 #include <com/sun/star/uri/XVndSunStarScriptUrlReference.hpp>
49 #include <com/sun/star/uri/UriReferenceFactory.hpp>
53 using namespace ::com::sun::star
;
54 using namespace ::com::sun::star::uno
;
55 using namespace ::com::sun::star::frame
;
56 using namespace ::com::sun::star::util
;
57 using namespace ::com::sun::star::beans
;
58 using namespace ::com::sun::star::lang
;
59 using namespace ::com::sun::star::script
;
60 using namespace ::com::sun::star::script::provider
;
61 using namespace ::com::sun::star::document
;
63 namespace scripting_protocolhandler
66 void SAL_CALL
ScriptProtocolHandler::initialize(
67 const css::uno::Sequence
< css::uno::Any
>& aArguments
)
74 // first argument contains a reference to the frame (may be empty or the desktop,
75 // but usually it's a "real" frame)
76 if ( aArguments
.hasElements() && !( aArguments
[ 0 ] >>= m_xFrame
) )
78 throw RuntimeException( "ScriptProtocolHandler::initialize: could not extract reference to the frame" );
81 ENSURE_OR_THROW( m_xContext
.is(), "ScriptProtocolHandler::initialize: No Service Manager available" );
82 m_bInitialised
= true;
85 Reference
< XDispatch
> SAL_CALL
ScriptProtocolHandler::queryDispatch(
86 const URL
& aURL
, const OUString
&, sal_Int32
)
88 Reference
< XDispatch
> xDispatcher
;
91 Reference
< uri::XUriReferenceFactory
> xFac
= uri::UriReferenceFactory::create( m_xContext
);
92 Reference
< uri::XUriReference
> uriRef
= xFac
->parse( aURL
.Complete
);
95 if ( uriRef
->getScheme() == "vnd.sun.star.script" )
104 Sequence
< Reference
< XDispatch
> > SAL_CALL
105 ScriptProtocolHandler::queryDispatches(
106 const Sequence
< DispatchDescriptor
>& seqDescriptor
)
108 sal_Int32 nCount
= seqDescriptor
.getLength();
109 Sequence
< Reference
< XDispatch
> > lDispatcher( nCount
);
110 std::transform(seqDescriptor
.begin(), seqDescriptor
.end(), lDispatcher
.getArray(),
111 [this](const DispatchDescriptor
& rDescr
) -> Reference
<XDispatch
> {
112 return queryDispatch(rDescr
.FeatureURL
, rDescr
.FrameName
, rDescr
.SearchFlags
); });
116 void SAL_CALL
ScriptProtocolHandler::dispatchWithNotification(
117 const URL
& aURL
, const Sequence
< PropertyValue
>& lArgs
,
118 const Reference
< XDispatchResultListener
>& xListener
)
120 if (officecfg::Office::Common::Security::Scripting::DisableMacrosExecution::get())
123 bool bSuccess
= false;
125 bool bCaughtException
= false;
128 if ( m_bInitialised
)
132 css::uno::Reference
<css::uri::XUriReferenceFactory
> urifac(
133 css::uri::UriReferenceFactory::create(m_xContext
));
134 css::uno::Reference
<css::uri::XVndSunStarScriptUrlReference
> uri(
135 urifac
->parse(aURL
.Complete
), css::uno::UNO_QUERY_THROW
);
136 auto const loc
= uri
->getParameter("location");
137 bool bIsDocumentScript
= loc
== "document";
139 if ( bIsDocumentScript
)
141 // obtain the component for our security check
142 Reference
< XEmbeddedScripts
> xDocumentScripts
;
143 if ( getScriptInvocation() )
144 xDocumentScripts
.set( m_xScriptInvocation
->getScriptContainer(), UNO_SET_THROW
);
146 OSL_ENSURE( xDocumentScripts
.is(), "ScriptProtocolHandler::dispatchWithNotification: can't do the security check!" );
147 if ( !xDocumentScripts
.is() || !xDocumentScripts
->getAllowMacroExecution() )
149 if ( xListener
.is() )
151 css::frame::DispatchResultEvent
aEvent(
153 css::frame::DispatchResultState::FAILURE
,
157 xListener
->dispatchFinished( aEvent
) ;
159 catch(const RuntimeException
&)
161 TOOLS_WARN_EXCEPTION("scripting",
162 "ScriptProtocolHandler::dispatchWithNotification: caught RuntimeException"
163 "while dispatchFinished with failure of the execution");
170 // Creates a ScriptProvider ( if one is not created already )
171 createScriptProvider();
173 Reference
< provider::XScript
> xFunc
=
174 m_xScriptProvider
->getScript( aURL
.Complete
);
175 ENSURE_OR_THROW( xFunc
.is(),
176 "ScriptProtocolHandler::dispatchWithNotification: validate xFunc - unable to obtain XScript interface" );
179 Sequence
< Any
> inArgs
;
180 Sequence
< Any
> outArgs
;
181 Sequence
< sal_Int16
> outIndex
;
183 if ( lArgs
.hasElements() )
186 for ( const auto& rArg
: lArgs
)
188 // Sometimes we get a propertyval with name = "Referer" or "SynchronMode". These
189 // are not actual arguments to be passed to script, but flags describing the
190 // call, so ignore. Who thought that passing such "meta-arguments" mixed in with
191 // real arguments was a good idea?
192 if ( (rArg
.Name
!= "Referer" &&
193 rArg
.Name
!= "SynchronMode") ||
194 rArg
.Name
.isEmpty() ) //TODO:???
196 inArgs
.realloc( ++argCount
);
197 inArgs
.getArray()[ argCount
- 1 ] = rArg
.Value
;
202 // attempt to protect the document against the script tampering with its Undo Context
203 std::unique_ptr
< ::framework::DocumentUndoGuard
> pUndoGuard
;
204 if ( bIsDocumentScript
)
205 pUndoGuard
.reset( new ::framework::DocumentUndoGuard( m_xScriptInvocation
) );
210 std::exception_ptr aFirstCaughtException
;
213 invokeResult
= xFunc
->invoke( inArgs
, outIndex
, outArgs
);
216 catch( const provider::ScriptFrameworkErrorException
& se
)
218 if (!aFirstCaughtException
)
219 aFirstCaughtException
= std::current_exception();
221 if ( se
.errorType
!= provider::ScriptFrameworkErrorType::NO_SUCH_SCRIPT
)
222 // the only condition which allows us to retry is if there is no method with the
223 // given name/signature
224 std::rethrow_exception(aFirstCaughtException
);
226 if ( !inArgs
.hasElements() )
227 // no chance to retry if we can't strip more in-args
228 std::rethrow_exception(aFirstCaughtException
);
230 // strip one argument, then retry
231 inArgs
.realloc( inArgs
.getLength() - 1 );
235 // Office doesn't handle exceptions rethrown here very well, it cores,
236 // all we can is log them and then set fail for the dispatch event!
237 // (if there is a listener of course)
238 catch ( const Exception
& e
)
240 aException
= ::cppu::getCaughtException();
242 invokeResult
<<= "ScriptProtocolHandler::dispatch: caught "
243 + aException
.getValueTypeName() + ": " + e
.Message
;
245 bCaughtException
= true;
250 invokeResult
<<= OUString(
251 "ScriptProtocolHandler::dispatchWithNotification failed, ScriptProtocolHandler not initialised"
255 if ( bCaughtException
)
257 SfxAbstractDialogFactory
* pFact
= SfxAbstractDialogFactory::Create();
258 pFact
->ShowAsyncScriptErrorDialog( nullptr, aException
);
261 if ( !xListener
.is() )
264 // always call dispatchFinished(), because we didn't load a document but
265 // executed a macro instead!
266 css::frame::DispatchResultEvent aEvent
;
268 aEvent
.Source
= getXWeak();
269 aEvent
.Result
= invokeResult
;
272 aEvent
.State
= css::frame::DispatchResultState::SUCCESS
;
276 aEvent
.State
= css::frame::DispatchResultState::FAILURE
;
281 xListener
->dispatchFinished( aEvent
) ;
283 catch(const RuntimeException
&)
285 TOOLS_WARN_EXCEPTION("scripting",
286 "ScriptProtocolHandler::dispatchWithNotification: caught RuntimeException"
287 "while dispatchFinished" );
291 void SAL_CALL
ScriptProtocolHandler::dispatch(
292 const URL
& aURL
, const Sequence
< PropertyValue
>& lArgs
)
294 dispatchWithNotification( aURL
, lArgs
, Reference
< XDispatchResultListener
>() );
297 void SAL_CALL
ScriptProtocolHandler::addStatusListener(
298 const Reference
< XStatusListener
>&, const URL
& )
300 // implement if status is supported
303 void SAL_CALL
ScriptProtocolHandler::removeStatusListener(
304 const Reference
< XStatusListener
>&, const URL
& )
308 ScriptProtocolHandler::getScriptInvocation()
310 if ( !m_xScriptInvocation
.is() && m_xFrame
.is() )
312 Reference
< XController
> xController
= m_xFrame
->getController();
313 if ( xController
.is() )
315 // try to obtain an XScriptInvocationContext interface, preferred from the
316 // mode, then from the controller
317 if ( !m_xScriptInvocation
.set( xController
->getModel(), UNO_QUERY
) )
318 m_xScriptInvocation
.set( xController
, UNO_QUERY
);
324 SfxFrame
* pFrame
= nullptr;
325 for ( pFrame
= SfxFrame::GetFirst(); pFrame
; pFrame
= SfxFrame::GetNext( *pFrame
) )
327 if ( pFrame
->GetFrameInterface() == m_xFrame
)
330 if (SfxObjectShell
* pDocShell
= pFrame
? pFrame
->GetCurrentDocument() : SfxObjectShell::Current())
332 Reference
< XModel
> xModel( pDocShell
->GetModel() );
333 m_xScriptInvocation
.set( xModel
, UNO_QUERY
);
338 return m_xScriptInvocation
.is();
341 void ScriptProtocolHandler::createScriptProvider()
343 if ( m_xScriptProvider
.is() )
348 // first, ask the component supporting the XScriptInvocationContext interface
349 // (if there is one) for a script provider
350 if ( getScriptInvocation() )
352 Reference
< XScriptProviderSupplier
> xSPS( m_xScriptInvocation
, UNO_QUERY
);
354 m_xScriptProvider
= xSPS
->getScriptProvider();
357 // second, ask the model in our frame
358 if ( !m_xScriptProvider
.is() && m_xFrame
.is() )
360 Reference
< XController
> xController
= m_xFrame
->getController();
361 if ( xController
.is() )
363 Reference
< XScriptProviderSupplier
> xSPS( xController
->getModel(), UNO_QUERY
);
365 m_xScriptProvider
= xSPS
->getScriptProvider();
370 // as a fallback, ask the controller
371 if ( !m_xScriptProvider
.is() && m_xFrame
.is() )
373 Reference
< XScriptProviderSupplier
> xSPS( m_xFrame
->getController(), UNO_QUERY
);
375 m_xScriptProvider
= xSPS
->getScriptProvider();
378 // if nothing of this is successful, use the master script provider
379 if ( !m_xScriptProvider
.is() )
381 Reference
< provider::XScriptProviderFactory
> xFac
=
382 provider::theMasterScriptProviderFactory::get( m_xContext
);
385 if ( getScriptInvocation() )
386 aContext
<<= m_xScriptInvocation
;
387 m_xScriptProvider
.set( xFac
->createScriptProvider( aContext
), UNO_SET_THROW
);
390 catch ( const Exception
& e
)
392 css::uno::Any anyEx
= cppu::getCaughtException();
393 throw css::lang::WrappedTargetRuntimeException(
394 "ScriptProtocolHandler::createScriptProvider: " + e
.Message
,
399 ScriptProtocolHandler::ScriptProtocolHandler( const Reference
< css::uno::XComponentContext
> & xContext
)
400 : m_bInitialised( false ), m_xContext( xContext
)
404 ScriptProtocolHandler::~ScriptProtocolHandler()
409 OUString SAL_CALL
ScriptProtocolHandler::getImplementationName( )
411 return "com.sun.star.comp.ScriptProtocolHandler";
415 sal_Bool SAL_CALL
ScriptProtocolHandler::supportsService(const OUString
& sServiceName
)
417 return cppu::supportsService(this, sServiceName
);
421 Sequence
< OUString
> SAL_CALL
ScriptProtocolHandler::getSupportedServiceNames()
423 return {"com.sun.star.frame.ProtocolHandler"};
426 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
427 scripting_ScriptProtocolHandler_get_implementation(
428 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const&)
430 return cppu::acquire(new ScriptProtocolHandler(context
));
433 } // namespace scripting_protocolhandler
436 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */