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 <framework/dispatchhelper.hxx>
21 #include <macros/xserviceinfo.hxx>
22 #include <services.h>
23 
24 #include <com/sun/star/frame/XNotifyingDispatch.hpp>
25 #include <com/sun/star/util/URLTransformer.hpp>
26 #include <com/sun/star/util/XURLTransformer.hpp>
27 
28 #include <comphelper/profilezone.hxx>
29 #include <unotools/mediadescriptor.hxx>
30 #include <vcl/threadex.hxx>
31 
32 namespace framework
33 {
34 // XInterface, XTypeProvider, XServiceInfo
35 
36 DEFINE_XSERVICEINFO_MULTISERVICE_2(DispatchHelper, ::cppu::OWeakObject,
37                                    "com.sun.star.frame.DispatchHelper",
38                                    IMPLEMENTATIONNAME_DISPATCHHELPER)
39 
40 DEFINE_INIT_SERVICE(DispatchHelper, {})
41 
42 /** ctor.
43 
44     @param xSMGR    the global uno service manager, which can be used to create own needed services.
45 */
DispatchHelper(const css::uno::Reference<css::uno::XComponentContext> & xContext)46 DispatchHelper::DispatchHelper(const css::uno::Reference<css::uno::XComponentContext>& xContext)
47     : m_xContext(xContext)
48 {
49 }
50 
51 /** dtor.
52 */
~DispatchHelper()53 DispatchHelper::~DispatchHelper() {}
54 
55 /** capsulate all steps of a dispatch request and provide so an easy way for dispatches.
56 
57     @param xDispatchProvider
58                 identifies the object, which provides may be valid dispatch objects for this execute.
59 
60     @param sURL
61                 describes the requested feature.
62 
63     @param sTargetFrameName
64                 points to the frame, which must be used (or may be created) for this dispatch.
65 
66     @param nSearchFlags
67                 in case the <var>sTargetFrameName</var> isn't unique, these flags regulate further searches.
68 
69     @param lArguments
70                 optional arguments for this request.
71 
72     @return An Any which capsulate a possible result of the internal wrapped dispatch.
73  */
executeDispatch(const css::uno::Reference<css::frame::XDispatchProvider> & xDispatchProvider,const OUString & sURL,const OUString & sTargetFrameName,sal_Int32 nSearchFlags,const css::uno::Sequence<css::beans::PropertyValue> & lArguments)74 css::uno::Any SAL_CALL DispatchHelper::executeDispatch(
75     const css::uno::Reference<css::frame::XDispatchProvider>& xDispatchProvider,
76     const OUString& sURL, const OUString& sTargetFrameName, sal_Int32 nSearchFlags,
77     const css::uno::Sequence<css::beans::PropertyValue>& lArguments)
78 {
79     // check for valid parameters
80     if ((!xDispatchProvider.is()) || (!m_xContext.is()) || (sURL.isEmpty()))
81     {
82         return css::uno::Any();
83     }
84 
85     // parse given URL
86     /* SAFE { */
87     osl::ClearableMutexGuard aReadLock(m_mutex);
88     css::uno::Reference<css::util::XURLTransformer> xParser
89         = css::util::URLTransformer::create(m_xContext);
90     aReadLock.clear();
91     /* } SAFE */
92 
93     css::util::URL aURL;
94     aURL.Complete = sURL;
95     xParser->parseStrict(aURL);
96 
97     // search dispatcher
98     css::uno::Reference<css::frame::XDispatch> xDispatch
99         = xDispatchProvider->queryDispatch(aURL, sTargetFrameName, nSearchFlags);
100 
101     utl::MediaDescriptor aDescriptor(lArguments);
102     bool bOnMainThread = aDescriptor.getUnpackedValueOrDefault("OnMainThread", false);
103 
104     if (bOnMainThread)
105         return vcl::solarthread::syncExecute([this, &xDispatch, &aURL, &lArguments]() {
106             return executeDispatch(xDispatch, aURL, true, lArguments);
107         });
108     else
109         return executeDispatch(xDispatch, aURL, true, lArguments);
110 }
111 
112 css::uno::Any
executeDispatch(const css::uno::Reference<css::frame::XDispatch> & xDispatch,const css::util::URL & aURL,bool SyncronFlag,const css::uno::Sequence<css::beans::PropertyValue> & lArguments)113 DispatchHelper::executeDispatch(const css::uno::Reference<css::frame::XDispatch>& xDispatch,
114                                 const css::util::URL& aURL, bool SyncronFlag,
115                                 const css::uno::Sequence<css::beans::PropertyValue>& lArguments)
116 {
117     comphelper::ProfileZone aZone("executeDispatch");
118     css::uno::Reference<css::uno::XInterface> xTHIS(static_cast<::cppu::OWeakObject*>(this),
119                                                     css::uno::UNO_QUERY);
120     m_aResult.clear();
121 
122     // check for valid parameters
123     if (xDispatch.is())
124     {
125         css::uno::Reference<css::frame::XNotifyingDispatch> xNotifyDispatch(xDispatch,
126                                                                             css::uno::UNO_QUERY);
127 
128         // make sure that synchronous execution is used (if possible)
129         css::uno::Sequence<css::beans::PropertyValue> aArguments(lArguments);
130         sal_Int32 nLength = lArguments.getLength();
131         aArguments.realloc(nLength + 1);
132         aArguments[nLength].Name = "SynchronMode";
133         aArguments[nLength].Value <<= SyncronFlag;
134 
135         if (xNotifyDispatch.is())
136         {
137             // dispatch it with guaranteed notification
138             // Here we can hope for a result ... instead of the normal dispatch.
139             css::uno::Reference<css::frame::XDispatchResultListener> xListener(xTHIS,
140                                                                                css::uno::UNO_QUERY);
141             /* SAFE { */
142             {
143                 osl::MutexGuard aWriteLock(m_mutex);
144                 m_xBroadcaster = xNotifyDispatch;
145                 m_aBlock.reset();
146             }
147             /* } SAFE */
148 
149             // dispatch it and wait for a notification
150             // TODO/MBA: waiting in main thread?!
151             xNotifyDispatch->dispatchWithNotification(aURL, aArguments, xListener);
152             m_aBlock.wait(); // wait for result
153         }
154         else
155         {
156             // dispatch it without any chance to get a result
157             xDispatch->dispatch(aURL, aArguments);
158         }
159     }
160 
161     return m_aResult;
162 }
163 
164 /** callback for started dispatch with guaranteed notifications.
165 
166     We must save the result, so the method executeDispatch() can return it.
167     Further we must release the broadcaster (otherwise it can't die)
168     and unblock the waiting executeDispatch() request.
169 
170     @param  aResult
171                 describes the result of the dispatch operation
172  */
dispatchFinished(const css::frame::DispatchResultEvent & aResult)173 void SAL_CALL DispatchHelper::dispatchFinished(const css::frame::DispatchResultEvent& aResult)
174 {
175     osl::MutexGuard g(m_mutex);
176     m_aResult <<= aResult;
177     m_aBlock.set();
178     m_xBroadcaster.clear();
179 }
180 
181 /** we have to release our broadcaster reference.
182 
183     @param aEvent
184                 describe the source of this event and MUST be our save broadcaster!
185  */
disposing(const css::lang::EventObject &)186 void SAL_CALL DispatchHelper::disposing(const css::lang::EventObject&)
187 {
188     osl::MutexGuard g(m_mutex);
189     m_aResult.clear();
190     m_aBlock.set();
191     m_xBroadcaster.clear();
192 }
193 }
194 
195 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
196