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