Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sfx2/source/appl/macroloader.cxx
Line
Count
Source
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 <config_features.h>
21
22
#include <macroloader.hxx>
23
24
#include <com/sun/star/frame/DispatchResultState.hpp>
25
#include <com/sun/star/frame/XModel3.hpp>
26
#include <basic/basmgr.hxx>
27
#include <basic/sbuno.hxx>
28
#include <basic/sberrors.hxx>
29
#include <cppuhelper/supportsservice.hxx>
30
#include <cppuhelper/weak.hxx>
31
#include <cppuhelper/weakref.hxx>
32
#include <framework/documentundoguard.hxx>
33
#include <sfx2/app.hxx>
34
#include <sfx2/frame.hxx>
35
#include <sfx2/objsh.hxx>
36
#include <tools/urlobj.hxx>
37
#include <vcl/svapp.hxx>
38
39
#include <memory>
40
41
using namespace ::com::sun::star;
42
using namespace ::com::sun::star::frame;
43
using namespace ::com::sun::star::lang;
44
using namespace ::com::sun::star::uno;
45
using namespace ::com::sun::star::util;
46
47
SfxMacroLoader::SfxMacroLoader(const css::uno::Sequence< css::uno::Any >& aArguments)
48
0
{
49
0
    Reference < XFrame > xFrame;
50
0
    if ( aArguments.hasElements() )
51
0
    {
52
0
        aArguments[0] >>= xFrame;
53
0
        m_xFrame = xFrame;
54
0
    }
55
0
}
56
57
OUString SAL_CALL SfxMacroLoader::getImplementationName()
58
0
{
59
0
    return u"com.sun.star.comp.sfx2.SfxMacroLoader"_ustr;
60
0
}
61
62
sal_Bool SAL_CALL SfxMacroLoader::supportsService(OUString const & ServiceName)
63
0
{
64
0
    return cppu::supportsService(this, ServiceName);
65
0
}
66
67
css::uno::Sequence<OUString> SAL_CALL SfxMacroLoader::getSupportedServiceNames()
68
0
{
69
0
    return { u"com.sun.star.frame.ProtocolHandler"_ustr };
70
0
}
71
72
SfxObjectShell* SfxMacroLoader::GetObjectShell(const Reference <XFrame>& xFrame)
73
0
{
74
0
    SfxObjectShell* pDocShell = nullptr;
75
76
0
    if ( xFrame.is() )
77
0
    {
78
0
        SfxFrame* pFrame=nullptr;
79
0
        for ( pFrame = SfxFrame::GetFirst(); pFrame; pFrame = SfxFrame::GetNext( *pFrame ) )
80
0
        {
81
0
            if ( pFrame->GetFrameInterface() == xFrame )
82
0
                break;
83
0
        }
84
85
0
        if ( pFrame )
86
0
            pDocShell = pFrame->GetCurrentDocument();
87
0
    }
88
89
0
    return pDocShell;
90
0
}
91
92
SfxObjectShell* SfxMacroLoader::GetObjectShell_Impl()
93
0
{
94
0
    Reference < XFrame > xFrame( m_xFrame.get(), UNO_QUERY );
95
0
    return SfxMacroLoader::GetObjectShell(xFrame);
96
0
}
97
98
uno::Reference<frame::XDispatch> SAL_CALL SfxMacroLoader::queryDispatch(
99
    const util::URL&   aURL            ,
100
    const OUString&               /*sTargetFrameName*/,
101
    sal_Int32                            /*nSearchFlags*/    )
102
0
{
103
0
    uno::Reference<frame::XDispatch> xDispatcher;
104
0
    if(aURL.Complete.startsWith("macro:"))
105
0
        xDispatcher = this;
106
0
    return xDispatcher;
107
0
}
108
109
110
uno::Sequence< uno::Reference<frame::XDispatch> > SAL_CALL
111
                SfxMacroLoader::queryDispatches( const uno::Sequence < frame::DispatchDescriptor >& seqDescriptor )
112
0
{
113
0
    sal_Int32 nCount = seqDescriptor.getLength();
114
0
    uno::Sequence< uno::Reference<frame::XDispatch> > lDispatcher(nCount);
115
0
    std::transform(seqDescriptor.begin(), seqDescriptor.end(), lDispatcher.getArray(),
116
0
        [this](const frame::DispatchDescriptor& rDescr) -> uno::Reference<frame::XDispatch> {
117
0
            return queryDispatch(rDescr.FeatureURL, rDescr.FrameName, rDescr.SearchFlags); });
118
0
    return lDispatcher;
119
0
}
120
121
122
void SAL_CALL SfxMacroLoader::dispatchWithNotification(
123
    const util::URL& aURL, const uno::Sequence<beans::PropertyValue>& /*lArgs*/,
124
    const uno::Reference<frame::XDispatchResultListener>& xListener )
125
0
{
126
0
    SolarMutexGuard aGuard;
127
128
0
    uno::Any aAny;
129
0
    ErrCode nErr = loadMacro( aURL.Complete, aAny, GetObjectShell_Impl() );
130
0
    if( !xListener.is() )
131
0
        return;
132
133
    // always call dispatchFinished(), because we didn't load a document but
134
    // executed a macro instead!
135
0
    frame::DispatchResultEvent aEvent;
136
137
0
    aEvent.Source = getXWeak();
138
0
    if( nErr == ERRCODE_NONE )
139
0
        aEvent.State = frame::DispatchResultState::SUCCESS;
140
0
    else
141
0
        aEvent.State = frame::DispatchResultState::FAILURE;
142
143
0
    xListener->dispatchFinished( aEvent ) ;
144
0
}
145
146
uno::Any SAL_CALL SfxMacroLoader::dispatchWithReturnValue(
147
    const util::URL& aURL, const uno::Sequence<beans::PropertyValue>& )
148
0
{
149
0
    uno::Any aRet;
150
0
    ErrCode nErr = loadMacro( aURL.Complete, aRet, GetObjectShell_Impl() );
151
152
    // aRet gets set to a different value only if nErr == ERRCODE_NONE
153
    // Return it in such case to preserve the original behaviour
154
155
    // In all other cases (nErr != ERRCODE_NONE), the calling code gets
156
    // the actual error code back
157
0
    if ( nErr != ERRCODE_NONE )
158
0
    {
159
0
        beans::PropertyValue aErrorCode;
160
161
0
        aErrorCode.Name = "ErrorCode";
162
0
        aErrorCode.Value <<= sal_uInt32(nErr);
163
164
0
        aRet <<= aErrorCode;
165
0
    }
166
167
0
    return aRet;
168
0
}
169
170
void SAL_CALL SfxMacroLoader::dispatch(
171
    const util::URL& aURL, const uno::Sequence<beans::PropertyValue>& /*lArgs*/ )
172
0
{
173
0
    SolarMutexGuard aGuard;
174
175
0
    uno::Any aAny;
176
0
    loadMacro( aURL.Complete, aAny, GetObjectShell_Impl() );
177
0
}
178
179
void SAL_CALL SfxMacroLoader::addStatusListener(
180
    const uno::Reference< frame::XStatusListener >& ,
181
    const util::URL&                                                    )
182
0
{
183
    /* TODO
184
            How we can handle different listener for further coming or currently running dispatch() jobs
185
            without any inconsistency!
186
     */
187
0
}
188
189
190
void SAL_CALL SfxMacroLoader::removeStatusListener(
191
    const uno::Reference< frame::XStatusListener >&,
192
    const util::URL&                                                  )
193
0
{
194
0
}
195
196
ErrCode SfxMacroLoader::loadMacro( const OUString& rURL, css::uno::Any& rRetval, SfxObjectShell* pSh )
197
0
{
198
0
#if !HAVE_FEATURE_SCRIPTING
199
0
    (void) rURL;
200
0
    (void) rRetval;
201
0
    (void) pSh;
202
0
    return ERRCODE_BASIC_PROC_UNDEFINED;
203
#else
204
    SfxObjectShell* pCurrent = pSh;
205
    if ( !pCurrent )
206
        // all not full qualified names use the BASIC of the given or current document
207
        pCurrent = SfxObjectShell::Current();
208
209
    // 'macro:///lib.mod.proc(args)' => macro of App-BASIC
210
    // 'macro://[docname|.]/lib.mod.proc(args)' => macro of current or qualified document
211
    // 'macro://obj.method(args)' => direct API call, execute it via App-BASIC
212
    const OUString& aMacro( rURL );
213
    sal_Int32 nThirdSlashPos = aMacro.indexOf( '/', 8 );
214
    sal_Int32 nArgsPos = aMacro.indexOf( '(' );
215
    BasicManager *pAppMgr = SfxApplication::GetBasicManager();
216
    BasicManager *pBasMgr = nullptr;
217
    ErrCode nErr = ERRCODE_NONE;
218
219
    // should a macro function be executed ( no direct API call)?
220
    if ( -1 != nThirdSlashPos && ( -1 == nArgsPos || nThirdSlashPos < nArgsPos ) )
221
    {
222
        // find BasicManager
223
        SfxObjectShell* pDoc = nullptr;
224
        OUString aBasMgrName( INetURLObject::decode(aMacro.subView( 8, nThirdSlashPos-8 ), INetURLObject::DecodeMechanism::WithCharset) );
225
        if ( aBasMgrName.isEmpty() )
226
            pBasMgr = pAppMgr;
227
        else if ( aBasMgrName == "." )
228
        {
229
            // current/actual document
230
            pDoc = pCurrent;
231
            if (pDoc)
232
                pBasMgr = pDoc->GetBasicManager();
233
        }
234
        else
235
        {
236
            // full qualified name, find document by name
237
            for ( SfxObjectShell *pObjSh = SfxObjectShell::GetFirst();
238
                    pObjSh && !pBasMgr;
239
                    pObjSh = SfxObjectShell::GetNext(*pObjSh) )
240
                if ( aBasMgrName == pObjSh->GetTitle(SFX_TITLE_APINAME) )
241
                {
242
                    pDoc = pObjSh;
243
                    pBasMgr = pDoc->GetBasicManager();
244
                }
245
        }
246
247
        if ( pBasMgr )
248
        {
249
            const bool bIsAppBasic = ( pBasMgr == pAppMgr );
250
            const bool bIsDocBasic = ( pBasMgr != pAppMgr );
251
252
            if ( pDoc )
253
            {
254
                // security check for macros from document basic if an SFX doc is given
255
                if ( !pDoc->AdjustMacroMode() )
256
                    // check forbids execution
257
                    return ERRCODE_IO_ACCESSDENIED;
258
            }
259
260
            // find BASIC method
261
            OUString aQualifiedMethod( INetURLObject::decode(aMacro.subView( nThirdSlashPos+1 ), INetURLObject::DecodeMechanism::WithCharset) );
262
            OUString aArgs;
263
            if ( -1 != nArgsPos )
264
            {
265
                // remove arguments from macro name
266
                aArgs = aQualifiedMethod.copy( nArgsPos - nThirdSlashPos - 1 );
267
                aQualifiedMethod = aQualifiedMethod.copy( 0, nArgsPos - nThirdSlashPos - 1 );
268
            }
269
270
            if ( pBasMgr->HasMacro( aQualifiedMethod ) )
271
            {
272
                Any aOldThisComponent;
273
                const bool bSetDocMacroMode = ( pDoc != nullptr ) && bIsDocBasic;
274
                const bool bSetGlobalThisComponent = ( pDoc != nullptr ) && bIsAppBasic;
275
                if ( bSetDocMacroMode )
276
                {
277
                    // mark document: it executes an own macro, so it's in a modal mode
278
                    pDoc->SetMacroMode_Impl();
279
                }
280
281
                if ( bSetGlobalThisComponent )
282
                {
283
                    // document is executed via AppBASIC, adjust ThisComponent variable
284
                    pAppMgr->SetGlobalUNOConstant( u"ThisComponent"_ustr, Any( pDoc->GetModel() ), &aOldThisComponent );
285
                }
286
287
                // just to let the shell be alive
288
                SfxObjectShellRef xKeepDocAlive = pDoc;
289
290
                {
291
                    // attempt to protect the document against the script tampering with its Undo Context
292
                    std::optional< ::framework::DocumentUndoGuard > pUndoGuard;
293
                    if ( bIsDocBasic )
294
                        pUndoGuard.emplace( pDoc->GetModel() );
295
296
                    // execute the method
297
                    SbxVariableRef retValRef = new SbxVariable;
298
                    nErr = pBasMgr->ExecuteMacro( aQualifiedMethod, aArgs, retValRef.get() );
299
                    if ( nErr == ERRCODE_NONE )
300
                        rRetval = sbxToUnoValue( retValRef.get() );
301
                }
302
303
                if ( bSetGlobalThisComponent )
304
                {
305
                    pAppMgr->SetGlobalUNOConstant( u"ThisComponent"_ustr, aOldThisComponent );
306
                }
307
308
                if ( bSetDocMacroMode )
309
                {
310
                    // remove flag for modal mode
311
                    pDoc->SetMacroMode_Impl( false );
312
                }
313
            }
314
            else
315
                nErr = ERRCODE_BASIC_PROC_UNDEFINED;
316
        }
317
        else
318
            nErr = ERRCODE_IO_NOTEXISTS;
319
    }
320
    else
321
    {
322
        // direct API call on a specified object
323
        OUString aCall =
324
            "[" +
325
            INetURLObject::decode(aMacro.subView(6),
326
                    INetURLObject::DecodeMechanism::WithCharset) +
327
            "]";
328
        pAppMgr->GetLib(0)->Execute(aCall);
329
        nErr = SbxBase::GetError();
330
    }
331
332
    SbxBase::ResetError();
333
    return nErr;
334
#endif
335
0
}
336
337
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
338
com_sun_star_comp_sfx2_SfxMacroLoader_get_implementation(
339
    css::uno::XComponentContext *,
340
    css::uno::Sequence<css::uno::Any> const &arguments)
341
0
{
342
0
    return cppu::acquire(new SfxMacroLoader(arguments));
343
0
}
344
345
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */