LibreOffice Module sfx2 (master) 1
eventsupplier.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#include <com/sun/star/beans/PropertyValue.hpp>
21
22#include <com/sun/star/util/URL.hpp>
23#include <com/sun/star/frame/Desktop.hpp>
24#include <com/sun/star/uno/Sequence.hxx>
25#include <com/sun/star/util/URLTransformer.hpp>
26#include <com/sun/star/util/XURLTransformer.hpp>
27#include <tools/urlobj.hxx>
28#include <svl/macitem.hxx>
29#include <sfx2/objsh.hxx>
30#include <sfx2/evntconf.hxx>
31#include <unotools/eventcfg.hxx>
32#include <sal/log.hxx>
33
37#include <officecfg/Office/Common.hxx>
38#include <eventsupplier.hxx>
39
40#include <sfx2/app.hxx>
41
42#include <sfx2/viewfrm.hxx>
43#include <sfx2/frame.hxx>
44#include <macroloader.hxx>
45
46#include <unicode/errorcode.h>
47#include <unicode/regex.h>
48#include <unicode/unistr.h>
49
50using namespace css;
51using namespace ::com::sun::star;
52
53
54
55 // --- XNameReplace ---
56
57void SAL_CALL SfxEvents_Impl::replaceByName( const OUString & aName, const uno::Any & rElement )
58{
59 std::unique_lock aGuard( maMutex );
60
61 // find the event in the list and replace the data
63 if (nIndex == -1)
64 throw container::NoSuchElementException();
65
66 // check for correct type of the element
68 throw lang::IllegalArgumentException();
69 ::comphelper::NamedValueCollection const aEventDescriptor( rElement );
70
71 // create Configuration at first, creation might call this method also and that would overwrite everything
72 // we might have stored before!
73 if ( mpObjShell && !mpObjShell->IsLoading() )
74 {
75 // SetModified will end up calling into our documentEventOccured method
76 aGuard.unlock();
78 aGuard.lock();
79 }
80
81 ::comphelper::NamedValueCollection aNormalizedDescriptor;
82 NormalizeMacro( aEventDescriptor, aNormalizedDescriptor, mpObjShell );
83
84 OUString sType;
85 if ( ( aNormalizedDescriptor.size() == 1 )
86 && !aNormalizedDescriptor.has( PROP_EVENT_TYPE ) //TODO
87 && ( aNormalizedDescriptor.get( PROP_EVENT_TYPE ) >>= sType )
88 && ( sType.isEmpty() )
89 )
90 {
91 // An empty event type means no binding. Therefore reset data
92 // to reflect that state.
93 // (that's for compatibility only. Nowadays, the Tools/Customize dialog should
94 // set an empty sequence to indicate the request for resetting the assignment.)
95 OSL_ENSURE( false, "legacy event assignment format detected" );
96 aNormalizedDescriptor.clear();
97 }
98
99 if ( !aNormalizedDescriptor.empty() )
100 {
101 maEventData[nIndex] = aNormalizedDescriptor.getPropertyValues();
102 }
103 else
104 {
105 maEventData[nIndex] = {};
106 }
107}
108
109
110// --- XNameAccess ---
111
112uno::Any SAL_CALL SfxEvents_Impl::getByName( const OUString& aName )
113{
114 std::unique_lock aGuard( maMutex );
115
116 // find the event in the list and return the data
117
119 if (nIndex != -1)
120 return uno::Any(maEventData[nIndex]);
121
122 throw container::NoSuchElementException();
123}
124
125
126uno::Sequence< OUString > SAL_CALL SfxEvents_Impl::getElementNames()
127{
128 return maEventNames;
129}
130
131
132sal_Bool SAL_CALL SfxEvents_Impl::hasByName( const OUString& aName )
133{
134 std::unique_lock aGuard( maMutex );
135
136 // find the event in the list and return the data
137
139}
140
141
142// --- XElementAccess ( parent of XNameAccess ) ---
143
145{
147 return aElementType;
148}
149
150
152{
153 std::unique_lock aGuard( maMutex );
154
155 return maEventNames.hasElements();
156}
157
158bool SfxEvents_Impl::isScriptURLAllowed(const OUString& aScriptURL)
159{
160 std::optional<css::uno::Sequence<OUString>> allowedEvents(
161 officecfg::Office::Common::Security::Scripting::AllowedDocumentEventURLs::get());
162 // When AllowedDocumentEventURLs is empty, all event URLs are allowed
163 if (!allowedEvents)
164 return true;
165
166 icu::ErrorCode status;
167 const uint32_t rMatcherFlags = UREGEX_CASE_INSENSITIVE;
168 icu::UnicodeString usInput(aScriptURL.getStr());
169 const css::uno::Sequence<OUString>& rAllowedEvents = *allowedEvents;
170 for (auto const& allowedEvent : rAllowedEvents)
171 {
172 icu::UnicodeString usRegex(allowedEvent.getStr());
173 icu::RegexMatcher rmatch1(usRegex, usInput, rMatcherFlags, status);
174 if (aScriptURL.startsWith(allowedEvent) || rmatch1.matches(status))
175 {
176 return true;
177 }
178 }
179
180 return false;
181}
182
183void SfxEvents_Impl::Execute( css::uno::Sequence < css::beans::PropertyValue > const & aProperties, const document::DocumentEvent& aTrigger, SfxObjectShell* pDoc )
184{
185 OUString aType;
186 OUString aScript;
187 OUString aLibrary;
188 OUString aMacroName;
189
190 if ( !aProperties.hasElements() )
191 return;
192
193 for ( const auto& rProp : std::as_const(aProperties) )
194 {
195 if ( rProp.Name == PROP_EVENT_TYPE )
196 rProp.Value >>= aType;
197 else if ( rProp.Name == PROP_SCRIPT )
198 rProp.Value >>= aScript;
199 else if ( rProp.Name == PROP_LIBRARY )
200 rProp.Value >>= aLibrary;
201 else if ( rProp.Name == PROP_MACRO_NAME )
202 rProp.Value >>= aMacroName;
203 else {
204 OSL_FAIL("Unknown property value!");
205 }
206 }
207
208 if (aType.isEmpty())
209 {
210 // Empty type means no active binding for the event. Just ignore do nothing.
211 return;
212 }
213
214 if (aScript.isEmpty())
215 return;
216
217 if (!isScriptURLAllowed(aScript))
218 return;
219
220 if (!pDoc)
222
224 return;
225
226 if (aType == STAR_BASIC)
227 {
228 uno::Any aAny;
229 SfxMacroLoader::loadMacro( aScript, aAny, pDoc );
230 }
231 else if (aType == "Service" || aType == "Script")
232 {
233 util::URL aURL;
234 uno::Reference < util::XURLTransformer > xTrans( util::URLTransformer::create( ::comphelper::getProcessComponentContext() ) );
235
236 aURL.Complete = aScript;
237 xTrans->parseStrict( aURL );
238
239 bool bAllowed = !SfxObjectShell::UnTrustedScript(aURL.Complete);
240
241 if (bAllowed)
242 {
244
245 uno::Reference
246 < frame::XDispatchProvider > xProv;
247
248 if ( pView != nullptr )
249 {
250 xProv = uno::Reference
251 < frame::XDispatchProvider > (
252 pView->GetFrame().GetFrameInterface(), uno::UNO_QUERY );
253 }
254 else
255 {
256 xProv = frame::Desktop::create( ::comphelper::getProcessComponentContext() );
257 }
258
259 uno::Reference < frame::XDispatch > xDisp;
260 if ( xProv.is() )
261 xDisp = xProv->queryDispatch( aURL, OUString(), 0 );
262
263 if ( xDisp.is() )
264 {
265 beans::PropertyValue aEventParam;
266 aEventParam.Value <<= aTrigger;
267 uno::Sequence< beans::PropertyValue > aDispatchArgs( &aEventParam, 1 );
268 xDisp->dispatch( aURL, aDispatchArgs );
269 }
270 }
271 }
272 else
273 {
274 SAL_WARN( "sfx.notify", "notifyEvent(): Unsupported event type" );
275 }
276}
277
278
279// --- ::document::XEventListener ---
280
281void SAL_CALL SfxEvents_Impl::documentEventOccured( const document::DocumentEvent& aEvent )
282{
283 std::unique_lock aGuard( maMutex );
284
285 // get the event name, find the corresponding data, execute the data
286
288 if ( nIndex == -1 )
289 return;
290
291 css::uno::Sequence < css::beans::PropertyValue > aEventData = maEventData[ nIndex ];
292 aGuard.unlock();
293 Execute( aEventData, aEvent, mpObjShell );
294}
295
296
297// --- ::lang::XEventListener ---
298
299void SAL_CALL SfxEvents_Impl::disposing( const lang::EventObject& /*Source*/ )
300{
301 std::unique_lock aGuard( maMutex );
302
303 if ( mxBroadcaster.is() )
304 {
305 mxBroadcaster->removeDocumentEventListener( this );
306 mxBroadcaster = nullptr;
307 }
308}
309
310
312 uno::Reference< document::XDocumentEventBroadcaster > const & xBroadcaster )
313{
314 // get the list of supported events and store it
315 if ( pShell )
316 maEventNames = pShell->GetEventNames();
317 else
319
320 maEventData.resize( maEventNames.getLength() );
321
322 mpObjShell = pShell;
323 mxBroadcaster = xBroadcaster;
324
325 if ( mxBroadcaster.is() )
326 mxBroadcaster->addDocumentEventListener( this );
327}
328
329
331{
332}
333
334
335std::unique_ptr<SvxMacro> SfxEvents_Impl::ConvertToMacro( const uno::Any& rElement, SfxObjectShell* pObjShell )
336{
337 std::unique_ptr<SvxMacro> pMacro;
338 uno::Sequence < beans::PropertyValue > aProperties;
339 uno::Any aAny;
340 NormalizeMacro( rElement, aAny, pObjShell );
341
342 if ( aAny >>= aProperties )
343 {
344 OUString aType;
345 OUString aScriptURL;
346 OUString aLibrary;
347 OUString aMacroName;
348
349 if ( !aProperties.hasElements() )
350 return pMacro;
351
352 for ( const auto& rProp : std::as_const(aProperties) )
353 {
354 if ( rProp.Name == PROP_EVENT_TYPE )
355 rProp.Value >>= aType;
356 else if ( rProp.Name == PROP_SCRIPT )
357 rProp.Value >>= aScriptURL;
358 else if ( rProp.Name == PROP_LIBRARY )
359 rProp.Value >>= aLibrary;
360 else if ( rProp.Name == PROP_MACRO_NAME )
361 rProp.Value >>= aMacroName;
362 else {
363 OSL_FAIL("Unknown property value!");
364 }
365 }
366
367 // Get the type
369 if ( aType == STAR_BASIC )
371 else if (aType == "Script" && !aScriptURL.isEmpty())
373 else if ( aType == SVX_MACRO_LANGUAGE_JAVASCRIPT )
375 else {
376 SAL_WARN( "sfx.notify", "ConvertToMacro: Unknown macro type" );
377 }
378
379 if ( !aMacroName.isEmpty() )
380 {
381 if ( aLibrary == "application" )
382 aLibrary = SfxGetpApp()->GetName();
383 else
384 aLibrary.clear();
385 pMacro.reset(new SvxMacro( aMacroName, aLibrary, eType ));
386 }
387 else if ( eType == EXTENDED_STYPE )
388 pMacro.reset(new SvxMacro( aScriptURL, aType ));
389 }
390
391 return pMacro;
392}
393
394void SfxEvents_Impl::NormalizeMacro( const uno::Any& rEvent, uno::Any& rRet, SfxObjectShell* pDoc )
395{
396 const ::comphelper::NamedValueCollection aEventDescriptor( rEvent );
397 ::comphelper::NamedValueCollection aEventDescriptorOut;
398
399 NormalizeMacro( aEventDescriptor, aEventDescriptorOut, pDoc );
400
401 rRet <<= aEventDescriptorOut.getPropertyValues();
402}
403
404void SfxEvents_Impl::NormalizeMacro( const ::comphelper::NamedValueCollection& i_eventDescriptor,
405 ::comphelper::NamedValueCollection& o_normalizedDescriptor, SfxObjectShell* i_document )
406{
407 SfxObjectShell* pDoc = i_document;
408 if ( !pDoc )
410
411 OUString aType = i_eventDescriptor.getOrDefault( PROP_EVENT_TYPE, OUString() );
412 OUString aScript = i_eventDescriptor.getOrDefault( PROP_SCRIPT, OUString() );
413 OUString aLibrary = i_eventDescriptor.getOrDefault( PROP_LIBRARY, OUString() );
414 OUString aMacroName = i_eventDescriptor.getOrDefault( PROP_MACRO_NAME, OUString() );
415
416 if ( !aType.isEmpty() )
417 o_normalizedDescriptor.put( PROP_EVENT_TYPE, aType );
418 if ( !aScript.isEmpty() )
419 o_normalizedDescriptor.put( PROP_SCRIPT, aScript );
420
421 if ( aType != STAR_BASIC )
422 return;
423
424 if ( !aScript.isEmpty() )
425 {
426 if ( aMacroName.isEmpty() || aLibrary.isEmpty() )
427 {
428 sal_Int32 nThirdSlashPos = aScript.indexOf( '/', 8 );
429 sal_Int32 nArgsPos = aScript.indexOf( '(' );
430 if ( ( nThirdSlashPos != -1 ) && ( nArgsPos == -1 || nThirdSlashPos < nArgsPos ) )
431 {
432 OUString aBasMgrName( INetURLObject::decode( aScript.subView( 8, nThirdSlashPos-8 ), INetURLObject::DecodeMechanism::WithCharset ) );
433 if (pDoc && aBasMgrName == ".")
434 aLibrary = pDoc->GetTitle();
435 else
436 aLibrary = SfxGetpApp()->GetName();
437
438 // Get the macro name
439 aMacroName = aScript.copy( nThirdSlashPos+1, nArgsPos - nThirdSlashPos - 1 );
440 }
441 else
442 {
443 SAL_WARN( "sfx.notify", "ConvertToMacro: Unknown macro url format" );
444 }
445 }
446 }
447 else if ( !aMacroName.isEmpty() )
448 {
449 aScript = "macro://";
450 if ( aLibrary != SfxGetpApp()->GetName() && aLibrary != "StarDesktop" && aLibrary != "application" )
451 aScript += ".";
452 aScript += "/" + aMacroName + "()";
453 }
454 else
455 // wrong properties
456 return;
457
458 if (aLibrary != "document")
459 {
460 if ( aLibrary.isEmpty() || (pDoc && ( aLibrary == pDoc->GetTitle( SFX_TITLE_APINAME ) || aLibrary == pDoc->GetTitle() )) )
461 aLibrary = "document";
462 else
463 aLibrary = "application";
464 }
465
466 o_normalizedDescriptor.put( PROP_SCRIPT, aScript );
467 o_normalizedDescriptor.put( PROP_LIBRARY, aLibrary );
468 o_normalizedDescriptor.put( PROP_MACRO_NAME, aMacroName );
469}
470
471/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OptionalString sType
PropertiesInfo aProperties
SfxApplication * SfxGetpApp()
Definition: app.hxx:231
AnyEventRef aEvent
static OUString decode(std::u16string_view rText, DecodeMechanism eMechanism, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
static std::unique_ptr< SvxMacro > ConvertToMacro(const css::uno::Any &rElement, SfxObjectShell *pDoc)
css::uno::Sequence< OUString > maEventNames
virtual ~SfxEvents_Impl() override
css::uno::Reference< css::document::XDocumentEventBroadcaster > mxBroadcaster
virtual css::uno::Any SAL_CALL getByName(const OUString &aName) override
virtual void SAL_CALL replaceByName(const OUString &aName, const css::uno::Any &aElement) override
virtual sal_Bool SAL_CALL hasElements() override
SfxEvents_Impl(SfxObjectShell *pShell, css::uno::Reference< css::document::XDocumentEventBroadcaster > const &xBroadcaster)
virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override
virtual css::uno::Type SAL_CALL getElementType() override
virtual void SAL_CALL documentEventOccured(const css::document::DocumentEvent &aEvent) override
static void NormalizeMacro(const css::uno::Any &rIn, css::uno::Any &rOut, SfxObjectShell *pDoc)
std::vector< css::uno::Sequence< css::beans::PropertyValue > > maEventData
virtual sal_Bool SAL_CALL hasByName(const OUString &aName) override
virtual void SAL_CALL disposing(const css::lang::EventObject &Source) override
SfxObjectShell * mpObjShell
static bool isScriptURLAllowed(const OUString &aScriptURL)
Check if script URL whitelist exists, and if so, if current script url is part of it.
static void Execute(css::uno::Sequence< css::beans::PropertyValue > const &aEventData, const css::document::DocumentEvent &aTrigger, SfxObjectShell *pDoc)
std::mutex maMutex
const css::uno::Reference< css::frame::XFrame > & GetFrameInterface() const
Definition: frame.cxx:515
static ErrCode loadMacro(const OUString &aURL, css::uno::Any &rRetval, SfxObjectShell *pDoc)
virtual css::uno::Sequence< OUString > GetEventNames()
Definition: objxtor.cxx:830
bool IsLoading() const
Definition: objmisc.cxx:1254
static bool UnTrustedScript(const OUString &rScriptURL)
Definition: objmisc.cxx:1365
static bool isScriptAccessAllowed(const css::uno::Reference< css::uno::XInterface > &rScriptContext)
Definition: objmisc.cxx:1344
css::uno::Reference< css::frame::XModel3 > GetModel() const
Definition: objxtor.cxx:838
OUString GetTitle(sal_uInt16 nMaxLen=0) const
Definition: objmisc.cxx:710
static SAL_WARN_UNUSED_RESULT SfxObjectShell * Current()
Definition: objxtor.cxx:481
virtual void SetModified(bool bModified=true)
Definition: objmisc.cxx:301
const OUString & GetName() const
Returns the name of the Shell object.
Definition: shell.cxx:119
static SAL_WARN_UNUSED_RESULT SfxViewFrame * GetFirst(const SfxObjectShell *pDoc=nullptr, bool bOnlyVisible=true)
Definition: viewfrm.cxx:1983
SfxFrame & GetFrame() const
Definition: viewfrm.cxx:2782
bool has(const OUString &_rValueName) const
const css::uno::Any & get(const OUString &_rValueName) const
static bool canExtractFrom(css::uno::Any const &i_value)
bool put(const OUString &_rValueName, const VALUE_TYPE &_rValue)
css::uno::Sequence< css::beans::PropertyValue > getPropertyValues() const
URL aURL
virtual OUString GetName() const override
#define STAR_BASIC
Definition: evntconf.hxx:92
constexpr OUStringLiteral PROP_MACRO_NAME
Definition: evntconf.hxx:91
constexpr OUStringLiteral PROP_SCRIPT
Definition: evntconf.hxx:90
constexpr OUStringLiteral PROP_EVENT_TYPE
Definition: evntconf.hxx:88
constexpr OUStringLiteral PROP_LIBRARY
Definition: evntconf.hxx:89
DocumentType eType
sal_Int32 nIndex
OUString aName
#define SAL_WARN(area, stream)
ScriptType
EXTENDED_STYPE
JAVASCRIPT
STARBASIC
constexpr OUStringLiteral SVX_MACRO_LANGUAGE_JAVASCRIPT
sal_Int32 findValue(const css::uno::Sequence< T1 > &_rList, const T2 &_rValue)
#define SFX_TITLE_APINAME
Definition: objsh.hxx:115
unsigned char sal_Bool