Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/xmloff/source/draw/eventimp.cxx
Line
Count
Source (jump to first uncovered line)
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/document/XEventsSupplier.hpp>
21
#include <com/sun/star/container/XNameReplace.hpp>
22
#include <tools/urlobj.hxx>
23
#include <osl/diagnose.h>
24
#include <sal/log.hxx>
25
#include <o3tl/string_view.hxx>
26
27
#include <sax/tools/converter.hxx>
28
29
#include <xmloff/xmltoken.hxx>
30
#include <xmloff/xmlimp.hxx>
31
#include <xmloff/xmlnamespace.hxx>
32
#include <xmloff/xmluconv.hxx>
33
#include <xmloff/namespacemap.hxx>
34
#include "eventimp.hxx"
35
36
using namespace ::com::sun::star;
37
using namespace ::com::sun::star::xml;
38
using namespace ::com::sun::star::xml::sax;
39
using namespace ::com::sun::star::uno;
40
using namespace ::com::sun::star::drawing;
41
using namespace ::com::sun::star::beans;
42
using namespace ::com::sun::star::document;
43
using namespace ::com::sun::star::container;
44
using namespace ::com::sun::star::presentation;
45
using namespace ::xmloff::token;
46
47
SvXMLEnumMapEntry<ClickAction> const aXML_EventActions_EnumMap[] =
48
{
49
    { XML_NONE,             ClickAction_NONE    },
50
    { XML_PREVIOUS_PAGE,    ClickAction_PREVPAGE },
51
    { XML_NEXT_PAGE,        ClickAction_NEXTPAGE },
52
    { XML_FIRST_PAGE,       ClickAction_FIRSTPAGE },
53
    { XML_LAST_PAGE,        ClickAction_LASTPAGE },
54
    { XML_HIDE,             ClickAction_INVISIBLE },
55
    { XML_STOP,             ClickAction_STOPPRESENTATION },
56
    { XML_EXECUTE,          ClickAction_PROGRAM },
57
    { XML_SHOW,             ClickAction_BOOKMARK },
58
    { XML_SHOW,             ClickAction_DOCUMENT },
59
    { XML_EXECUTE_MACRO,    ClickAction_MACRO },
60
    { XML_VERB,             ClickAction_VERB },
61
    { XML_FADE_OUT,         ClickAction_VANISH },
62
    { XML_SOUND,            ClickAction_SOUND },
63
    { XML_TOKEN_INVALID, ClickAction(0) }
64
};
65
66
SdXMLEventContextData::SdXMLEventContextData(const Reference< XShape >& rxShape)
67
0
    : mxShape(rxShape), mbValid(false), mbScript(false)
68
0
    , meClickAction(ClickAction_NONE), meEffect(EK_none)
69
0
    , meDirection(ED_none), mnStartScale(100), meSpeed(AnimationSpeed_MEDIUM)
70
0
    , mnVerb(0), mbPlayFull(false)
71
0
{
72
0
}
73
74
namespace {
75
76
class SdXMLEventContext : public SvXMLImportContext
77
{
78
public:
79
    SdXMLEventContextData maData;
80
81
public:
82
83
    SdXMLEventContext( SvXMLImport& rImport, sal_Int32 nElement, const Reference< XFastAttributeList>& xAttrList, const Reference< XShape >& rxShape );
84
85
    virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
86
        sal_Int32 nElement,
87
        const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
88
    virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
89
};
90
91
class XMLEventSoundContext : public SvXMLImportContext
92
{
93
public:
94
95
    XMLEventSoundContext( SvXMLImport& rImport, const Reference< XFastAttributeList >& xAttrList, SdXMLEventContext* pParent );
96
};
97
98
}
99
100
XMLEventSoundContext::XMLEventSoundContext( SvXMLImport& rImp, const Reference< XFastAttributeList >& xAttrList, SdXMLEventContext* pParent )
101
0
: SvXMLImportContext( rImp )
102
0
{
103
0
    for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
104
0
    {
105
0
        switch( aIter.getToken() )
106
0
        {
107
0
            case XML_ELEMENT(XLINK, XML_HREF):
108
0
                pParent->maData.msSoundURL = rImp.GetAbsoluteReference(aIter.toString());
109
0
                break;
110
0
            case XML_ELEMENT(PRESENTATION, XML_PLAY_FULL):
111
0
                pParent->maData.mbPlayFull = IsXMLToken( aIter, XML_TRUE );
112
0
                break;
113
0
            default:
114
0
                XMLOFF_WARN_UNKNOWN("xmloff", aIter);
115
0
        }
116
0
    }
117
0
}
118
119
SdXMLEventContext::SdXMLEventContext( SvXMLImport& rImp,
120
    sal_Int32 nElement,
121
    const Reference< XFastAttributeList >& xAttrList, const Reference< XShape >& rxShape )
122
0
    : SvXMLImportContext(rImp)
123
0
    , maData(rxShape)
124
0
{
125
0
    if( nElement == XML_ELEMENT(PRESENTATION, XML_EVENT_LISTENER) )
126
0
    {
127
0
        maData.mbValid = true;
128
0
    }
129
0
    else if( nElement == XML_ELEMENT(SCRIPT, XML_EVENT_LISTENER) )
130
0
    {
131
0
        maData.mbScript = true;
132
0
        maData.mbValid = true;
133
0
    }
134
0
    else
135
0
    {
136
0
        return;
137
0
    }
138
139
    // read attributes
140
0
    OUString sEventName;
141
0
    for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
142
0
    {
143
0
        switch( aIter.getToken() )
144
0
        {
145
0
        case XML_ELEMENT(PRESENTATION, XML_ACTION):
146
0
            SvXMLUnitConverter::convertEnum( maData.meClickAction, aIter.toView(), aXML_EventActions_EnumMap );
147
0
            break;
148
0
        case XML_ELEMENT(PRESENTATION, XML_EFFECT):
149
0
            SvXMLUnitConverter::convertEnum( maData.meEffect, aIter.toView(), aXML_AnimationEffect_EnumMap );
150
0
            break;
151
0
        case XML_ELEMENT(PRESENTATION, XML_DIRECTION):
152
0
            SvXMLUnitConverter::convertEnum( maData.meDirection, aIter.toView(), aXML_AnimationDirection_EnumMap );
153
0
            break;
154
0
        case XML_ELEMENT(PRESENTATION, XML_START_SCALE):
155
0
            {
156
0
                sal_Int32 nScale;
157
0
                if (::sax::Converter::convertPercent( nScale, aIter.toView() ))
158
0
                    maData.mnStartScale = static_cast<sal_Int16>(nScale);
159
0
            }
160
0
            break;
161
0
        case XML_ELEMENT(PRESENTATION, XML_SPEED):
162
0
            SvXMLUnitConverter::convertEnum( maData.meSpeed, aIter.toView(), aXML_AnimationSpeed_EnumMap );
163
0
            break;
164
0
        case XML_ELEMENT(PRESENTATION, XML_VERB):
165
0
            ::sax::Converter::convertNumber( maData.mnVerb, aIter.toView() );
166
0
            break;
167
0
        case XML_ELEMENT(SCRIPT, XML_EVENT_NAME):
168
0
            {
169
0
                sEventName = aIter.toString();
170
0
                sal_uInt16 nScriptPrefix =
171
0
                    GetImport().GetNamespaceMap().GetKeyByAttrValueQName(sEventName, &sEventName);
172
0
                maData.mbValid = XML_NAMESPACE_DOM == nScriptPrefix && sEventName == "click";
173
0
            }
174
0
            break;
175
0
        case XML_ELEMENT(SCRIPT, XML_LANGUAGE):
176
0
            {
177
                // language is not evaluated!
178
0
                OUString aScriptLanguage;
179
0
                maData.msLanguage = aIter.toString();
180
0
                sal_uInt16 nScriptPrefix = rImp.GetNamespaceMap().
181
0
                    GetKeyByAttrValueQName(maData.msLanguage, &aScriptLanguage);
182
0
                if( XML_NAMESPACE_OOO == nScriptPrefix )
183
0
                    maData.msLanguage = aScriptLanguage;
184
0
            }
185
0
            break;
186
0
        case XML_ELEMENT(SCRIPT, XML_MACRO_NAME):
187
0
            maData.msMacroName = aIter.toString();
188
0
            break;
189
0
        case XML_ELEMENT(XLINK, XML_HREF):
190
0
            {
191
0
                if ( maData.mbScript )
192
0
                {
193
0
                    maData.msMacroName = aIter.toString();
194
0
                }
195
0
                else
196
0
                {
197
0
                    const OUString aTmp =
198
0
                        rImp.GetAbsoluteReference(aIter.toString());
199
0
                    INetURLObject::translateToInternal( aTmp, maData.msBookmark,
200
0
                        INetURLObject::DecodeMechanism::Unambiguous );
201
0
                }
202
0
            }
203
0
            break;
204
0
        }
205
0
    }
206
207
0
    if( maData.mbValid )
208
0
        maData.mbValid = !sEventName.isEmpty();
209
210
0
    if (!maData.msMacroName.isEmpty())
211
0
        rImp.NotifyMacroEventRead();
212
0
}
213
214
css::uno::Reference< css::xml::sax::XFastContextHandler > SdXMLEventContext::createFastChildContext(
215
    sal_Int32 nElement,
216
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
217
0
{
218
0
    if( nElement == XML_ELEMENT(PRESENTATION, XML_SOUND) )
219
0
        return new XMLEventSoundContext( GetImport(), xAttrList, this );
220
0
    else
221
0
        XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
222
0
    return nullptr;
223
0
}
224
225
void SdXMLEventContext::endFastElement(sal_Int32 )
226
0
{
227
0
    GetImport().GetShapeImport()->addShapeEvents(maData);
228
0
}
229
230
void SdXMLEventContextData::ApplyProperties()
231
0
{
232
0
    if( !mbValid )
233
0
        return;
234
235
0
    do
236
0
    {
237
0
        Reference< XEventsSupplier > xEventsSupplier( mxShape, UNO_QUERY );
238
0
        if( !xEventsSupplier.is() )
239
0
            break;
240
241
0
        Reference< XNameReplace > xEvents( xEventsSupplier->getEvents() );
242
0
        SAL_WARN_IF( !xEvents.is(), "xmloff", "XEventsSupplier::getEvents() returned NULL" );
243
0
        if( !xEvents.is() )
244
0
            break;
245
246
0
        OUString sAPIEventName;
247
0
        uno::Sequence< beans::PropertyValue > aProperties;
248
249
0
        sAPIEventName = "OnClick";
250
251
0
        if( mbScript )
252
0
            meClickAction = ClickAction_MACRO;
253
254
0
        sal_Int32 nPropertyCount = 2;
255
0
        switch( meClickAction )
256
0
        {
257
0
            case ClickAction_NONE:
258
0
            case ClickAction_PREVPAGE:
259
0
            case ClickAction_NEXTPAGE:
260
0
            case ClickAction_FIRSTPAGE:
261
0
            case ClickAction_LASTPAGE:
262
0
            case ClickAction_INVISIBLE:
263
0
            case ClickAction_STOPPRESENTATION:
264
0
                break;
265
0
            case ClickAction_PROGRAM:
266
0
            case ClickAction_VERB:
267
0
            case ClickAction_BOOKMARK:
268
0
            case ClickAction_DOCUMENT:
269
0
                nPropertyCount += 1;
270
0
                break;
271
0
            case ClickAction_MACRO:
272
0
                if ( msLanguage.equalsIgnoreAsciiCase("starbasic") )
273
0
                    nPropertyCount += 1;
274
0
                break;
275
276
0
            case ClickAction_SOUND:
277
0
                nPropertyCount += 2;
278
0
                break;
279
280
0
            case ClickAction_VANISH:
281
0
                nPropertyCount += 4;
282
0
                break;
283
0
            default:
284
0
                break;
285
0
        }
286
287
0
        aProperties.realloc( nPropertyCount );
288
0
        beans::PropertyValue* pProperties = aProperties.getArray();
289
290
0
        if( ClickAction_MACRO == meClickAction )
291
0
        {
292
0
            if ( msLanguage.equalsIgnoreAsciiCase("starbasic") )
293
0
            {
294
0
                OUString sLibrary;
295
0
                const OUString& rApp = GetXMLToken( XML_APPLICATION );
296
0
                const OUString& rDoc = GetXMLToken( XML_DOCUMENT );
297
0
                if( msMacroName.getLength() > rApp.getLength()+1 &&
298
0
                    o3tl::equalsIgnoreAsciiCase(msMacroName.subView(0,rApp.getLength()), rApp) &&
299
0
                    ':' == msMacroName[rApp.getLength()] )
300
0
                {
301
0
                    sLibrary = "StarOffice";
302
0
                    msMacroName = msMacroName.copy( rApp.getLength()+1 );
303
0
                }
304
0
                else if( msMacroName.getLength() > rDoc.getLength()+1 &&
305
0
                    o3tl::equalsIgnoreAsciiCase(msMacroName.subView(0,rDoc.getLength()), rDoc) &&
306
0
                    ':' == msMacroName[rDoc.getLength()] )
307
0
                {
308
0
                    sLibrary = rDoc;
309
0
                    msMacroName = msMacroName.copy( rDoc.getLength()+1 );
310
0
                }
311
312
0
                pProperties->Name = "EventType";
313
0
                pProperties->Handle = -1;
314
0
                pProperties->Value <<= u"StarBasic"_ustr;
315
0
                pProperties->State = beans::PropertyState_DIRECT_VALUE;
316
0
                pProperties++;
317
318
0
                pProperties->Name = "MacroName";
319
0
                pProperties->Handle = -1;
320
0
                pProperties->Value <<= msMacroName;
321
0
                pProperties->State = beans::PropertyState_DIRECT_VALUE;
322
0
                pProperties++;
323
324
0
                pProperties->Name = "Library";
325
0
                pProperties->Handle = -1;
326
0
                pProperties->Value <<= sLibrary;
327
0
                pProperties->State = beans::PropertyState_DIRECT_VALUE;
328
0
            }
329
0
            else
330
0
            {
331
0
                pProperties->Name = "EventType";
332
0
                pProperties->Handle = -1;
333
0
                pProperties->Value <<= u"Script"_ustr;
334
0
                pProperties->State = beans::PropertyState_DIRECT_VALUE;
335
0
                pProperties++;
336
337
0
                pProperties->Name = "Script";
338
0
                pProperties->Handle = -1;
339
0
                pProperties->Value <<= msMacroName;
340
0
                pProperties->State = beans::PropertyState_DIRECT_VALUE;
341
0
            }
342
0
        }
343
0
        else
344
0
        {
345
0
            pProperties->Name = "EventType";
346
0
            pProperties->Handle = -1;
347
0
            pProperties->Value <<= u"Presentation"_ustr;
348
0
            pProperties->State = beans::PropertyState_DIRECT_VALUE;
349
0
            pProperties++;
350
351
            // ClickAction_BOOKMARK and ClickAction_DOCUMENT share the same xml event
352
            // so check here if it's really a bookmark or maybe a document
353
0
            if( meClickAction == ClickAction_BOOKMARK )
354
0
            {
355
0
                if( !msBookmark.startsWith( "#" ) )
356
0
                    meClickAction = ClickAction_DOCUMENT;
357
0
            }
358
359
0
            pProperties->Name = "ClickAction";
360
0
            pProperties->Handle = -1;
361
0
            pProperties->Value <<= meClickAction;
362
0
            pProperties->State = beans::PropertyState_DIRECT_VALUE;
363
0
            pProperties++;
364
365
0
            switch( meClickAction )
366
0
            {
367
0
                case ClickAction_NONE:
368
0
                case ClickAction_PREVPAGE:
369
0
                case ClickAction_NEXTPAGE:
370
0
                case ClickAction_FIRSTPAGE:
371
0
                case ClickAction_LASTPAGE:
372
0
                case ClickAction_INVISIBLE:
373
0
                case ClickAction_STOPPRESENTATION:
374
0
                    break;
375
376
0
                case ClickAction_BOOKMARK:
377
0
                    msBookmark = msBookmark.copy(1);
378
379
0
                    [[fallthrough]];
380
381
0
                case ClickAction_DOCUMENT:
382
0
                case ClickAction_PROGRAM:
383
0
                    pProperties->Name = "Bookmark";
384
0
                    pProperties->Handle = -1;
385
0
                    pProperties->Value <<= msBookmark;
386
0
                    pProperties->State = beans::PropertyState_DIRECT_VALUE;
387
0
                    break;
388
389
0
                case ClickAction_VANISH:
390
0
                    pProperties->Name = "Effect";
391
0
                    pProperties->Handle = -1;
392
0
                    pProperties->Value <<= ImplSdXMLgetEffect( meEffect, meDirection, mnStartScale, true );
393
0
                    pProperties->State = beans::PropertyState_DIRECT_VALUE;
394
0
                    pProperties++;
395
396
0
                    pProperties->Name = "Speed";
397
0
                    pProperties->Handle = -1;
398
0
                    pProperties->Value <<= meSpeed;
399
0
                    pProperties->State = beans::PropertyState_DIRECT_VALUE;
400
0
                    pProperties++;
401
402
0
                    [[fallthrough]];
403
404
0
                case ClickAction_SOUND:
405
0
                    pProperties->Name = "SoundURL";
406
0
                    pProperties->Handle = -1;
407
0
                    pProperties->Value <<= msSoundURL;
408
0
                    pProperties->State = beans::PropertyState_DIRECT_VALUE;
409
0
                    pProperties++;
410
411
0
                    pProperties->Name = "PlayFull";
412
0
                    pProperties->Handle = -1;
413
0
                    pProperties->Value <<= mbPlayFull;
414
0
                    pProperties->State = beans::PropertyState_DIRECT_VALUE;
415
0
                    break;
416
417
0
                case ClickAction_VERB:
418
0
                    pProperties->Name = "Verb";
419
0
                    pProperties->Handle = -1;
420
0
                    pProperties->Value <<= mnVerb;
421
0
                    pProperties->State = beans::PropertyState_DIRECT_VALUE;
422
0
                    break;
423
0
                case ClickAction_MACRO:
424
0
                    OSL_FAIL("xmloff::SdXMLEventContext::EndElement(), ClickAction_MACRO must be handled in different if case");
425
0
                    break;
426
0
                default:
427
0
                    break;
428
0
            }
429
0
        }
430
0
        xEvents->replaceByName( sAPIEventName, uno::Any( aProperties ) );
431
432
0
    } while(false);
433
0
}
434
435
436
SdXMLEventsContext::SdXMLEventsContext( SvXMLImport& rImport, const Reference< XShape >& rxShape)
437
0
: SvXMLImportContext(rImport), mxShape( rxShape )
438
0
{
439
0
}
440
441
SdXMLEventsContext::~SdXMLEventsContext()
442
0
{
443
0
}
444
445
css::uno::Reference< css::xml::sax::XFastContextHandler > SdXMLEventsContext::createFastChildContext(
446
    sal_Int32 nElement,
447
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
448
0
{
449
0
    return new SdXMLEventContext( GetImport(), nElement, xAttrList, mxShape );
450
0
}
451
452
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */