LibreOffice Module framework (master) 1
dispatchprovider.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
21#include <loadenv/loadenv.hxx>
25
26#include <pattern/window.hxx>
27#include <targets.h>
29
30#include <com/sun/star/frame/XDesktop.hpp>
31#include <com/sun/star/frame/FrameSearchFlag.hpp>
32#include <com/sun/star/uno/Exception.hpp>
33#include <com/sun/star/lang/XMultiServiceFactory.hpp>
34#include <com/sun/star/lang/XInitialization.hpp>
35#include <com/sun/star/util/XCacheInfo.hpp>
36
37#include <rtl/ustring.hxx>
38#include <utility>
39#include <vcl/svapp.hxx>
40#include <sal/log.hxx>
42
43namespace framework{
44
59DispatchProvider::DispatchProvider( css::uno::Reference< css::uno::XComponentContext > xContext ,
60 const css::uno::Reference< css::frame::XFrame >& xFrame )
61 : m_xContext (std::move( xContext ))
62 , m_xFrame ( xFrame )
63{
64}
65
71{
72}
73
93css::uno::Reference< css::frame::XDispatch > SAL_CALL DispatchProvider::queryDispatch( const css::util::URL& aURL ,
94 const OUString& sTargetFrameName ,
95 sal_Int32 nSearchFlags )
96{
97 css::uno::Reference< css::frame::XDispatch > xDispatcher;
98
99 css::uno::Reference< css::frame::XFrame > xOwner(m_xFrame);
100
101 css::uno::Reference< css::frame::XDesktop > xDesktopCheck( xOwner, css::uno::UNO_QUERY );
102
103 if (xDesktopCheck.is())
104 xDispatcher = implts_queryDesktopDispatch(xOwner, aURL, sTargetFrameName, nSearchFlags);
105 else
106 xDispatcher = implts_queryFrameDispatch(xOwner, aURL, sTargetFrameName, nSearchFlags);
107
108 return xDispatcher;
109}
110
126css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL DispatchProvider::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptions )
127{
128 // Create return list - which must have same size then the given descriptor
129 // It's not allowed to pack it!
130 sal_Int32 nCount = lDescriptions.getLength();
131 css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount );
132 auto lDispatcherRange = asNonConstRange(lDispatcher);
133 // Step over all descriptors and try to get any dispatcher for it.
134 for( sal_Int32 i=0; i<nCount; ++i )
135 {
136 lDispatcherRange[i] = queryDispatch( lDescriptions[i].FeatureURL ,
137 lDescriptions[i].FrameName ,
138 lDescriptions[i].SearchFlags );
139 }
140
141 return lDispatcher;
142}
143
151css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_queryDesktopDispatch( const css::uno::Reference< css::frame::XFrame >& xDesktop ,
152 const css::util::URL& aURL ,
153 const OUString& sTargetFrameName ,
154 sal_Int32 nSearchFlags )
155{
156 css::uno::Reference< css::frame::XDispatch > xDispatcher;
157
158 // ignore wrong requests which are not supported
159 if (
160 (sTargetFrameName==SPECIALTARGET_PARENT ) || // we have no parent by definition
161 (sTargetFrameName==SPECIALTARGET_BEAMER ) // beamer frames are allowed as child of tasks only -
162 // and they exist more than ones. We have no idea which our sub tasks is the right one
163 )
164 {
165 return nullptr;
166 }
167
168 // I) handle special cases which not right for using findFrame() first
169
170 // I.I) "_blank"
171 // It's not the right place to create a new task here - because we are queried for a dispatch object
172 // only, which can handle such request. Such dispatcher should create the required task on demand.
173 // Normally the functionality for "_blank" is provided by findFrame() - but that would create it directly
174 // here. that's why we must "intercept" here.
175
176 if (sTargetFrameName==SPECIALTARGET_BLANK)
177 {
179 xDispatcher = implts_getOrCreateDispatchHelper( E_BLANKDISPATCHER, xDesktop );
180 }
181
182 // I.II) "_default"
183 // This is a combination of search an empty task for recycling - or create a new one.
184
185 else if (sTargetFrameName==SPECIALTARGET_DEFAULT)
186 {
188 xDispatcher = implts_getOrCreateDispatchHelper( E_DEFAULTDISPATCHER, xDesktop );
189
192 }
193
194 // I.III) "_self", "", "_top"
195 // The desktop can't load any document - but he can handle some special protocols like "uno", "slot" ...
196 // Why is "top" here handled too? Because the desktop is the topest frame. Normally it's superfluous
197 // to use this target - but we can handle it in the same manner then "_self".
198
199 else if (
200 (sTargetFrameName==SPECIALTARGET_SELF) ||
201 (sTargetFrameName==SPECIALTARGET_TOP ) ||
202 (sTargetFrameName.isEmpty())
203 )
204 {
205 xDispatcher = implts_searchProtocolHandler(aURL);
206 }
207
208 // I.IV) no further special targets exist
209 // Now we have to search for the right target frame by calling findFrame() - but should provide our code
210 // against creation of a new task if no frame could be found.
211 // I said it before - it's allowed for dispatch() only.
212
213 else
214 {
215 sal_Int32 nRightFlags = nSearchFlags & ~css::frame::FrameSearchFlag::CREATE;
216
217 // try to find any existing target and ask him for his dispatcher
218 css::uno::Reference< css::frame::XFrame > xFoundFrame = xDesktop->findFrame(sTargetFrameName, nRightFlags);
219 if (xFoundFrame.is())
220 {
221 css::uno::Reference< css::frame::XDispatchProvider > xProvider( xFoundFrame, css::uno::UNO_QUERY );
222 xDispatcher = xProvider->queryDispatch(aURL,SPECIALTARGET_SELF,0);
223 }
224 // if it couldn't be found - but creation was allowed
225 // use special dispatcher for creation or forwarding to the browser
226 else if (nSearchFlags & css::frame::FrameSearchFlag::CREATE)
227 xDispatcher = implts_getOrCreateDispatchHelper( E_CREATEDISPATCHER, xDesktop, sTargetFrameName, nSearchFlags );
228 }
229
230 return xDispatcher;
231}
232
233css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_queryFrameDispatch( const css::uno::Reference< css::frame::XFrame >& xFrame ,
234 const css::util::URL& aURL ,
235 const OUString& sTargetFrameName ,
236 sal_Int32 nSearchFlags )
237{
238 css::uno::Reference< css::frame::XDispatch > xDispatcher;
239
240 // 0) Some URLs are dispatched in a generic way (e.g. by the menu) using the default target "".
241 // But they are specified to use her own fix target. Detect such URLs here and use the correct target.
242
243 // I) handle special cases which not right for using findFrame() first
244
245 // I.I) "_blank", "_default"
246 // It's not the right place to create a new task here. Only the desktop can do that.
247 // Normally the functionality for "_blank" is provided by findFrame() - but that would create it directly
248 // here. that's why we must "intercept" here.
249
250 if (
251 (sTargetFrameName==SPECIALTARGET_BLANK ) ||
252 (sTargetFrameName==SPECIALTARGET_DEFAULT)
253 )
254 {
255 css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY );
256 if (xParent.is())
257 xDispatcher = xParent->queryDispatch(aURL, sTargetFrameName, 0); // it's a special target - ignore search flags
258 }
259
260 // I.II) "_beamer"
261 // Special sub frame of a top frame only. Search or create it. ... OK it's currently a little bit HACKI.
262 // Only the sfx (means the controller) can create it.
263
264 else if (sTargetFrameName==SPECIALTARGET_BEAMER)
265 {
266 css::uno::Reference< css::frame::XDispatchProvider > xBeamer( xFrame->findFrame( SPECIALTARGET_BEAMER, css::frame::FrameSearchFlag::CHILDREN | css::frame::FrameSearchFlag::SELF ), css::uno::UNO_QUERY );
267 if (xBeamer.is())
268 {
269 xDispatcher = xBeamer->queryDispatch(aURL, SPECIALTARGET_SELF, 0);
270 }
271 else
272 {
273 css::uno::Reference< css::frame::XDispatchProvider > xController( xFrame->getController(), css::uno::UNO_QUERY );
274 if (xController.is())
275 // force using of special target - but use original search flags
276 // May the caller used the CREATE flag or not!
277 xDispatcher = xController->queryDispatch(aURL, SPECIALTARGET_BEAMER, nSearchFlags);
278 }
279 }
280
281 // I.IV) "_parent"
282 // Our parent frame (if it exist) should handle this URL.
283
284 else if (sTargetFrameName==SPECIALTARGET_PARENT)
285 {
286 css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY );
287 if (xParent.is())
288 // SELF => we must address the parent directly... and not his parent or any other parent!
289 xDispatcher = xParent->queryDispatch(aURL, SPECIALTARGET_SELF, 0);
290 }
291
292 // I.V) "_top"
293 // This request must be forwarded to any parent frame, till we reach a top frame.
294 // If no parent exist, we can handle itself.
295
296 else if (sTargetFrameName==SPECIALTARGET_TOP)
297 {
298 if (xFrame->isTop())
299 {
300 // If we are this top frame itself (means our owner frame)
301 // we should call ourself recursiv with a better target "_self".
302 // So we can share the same code! (see reaction for "_self" inside this method too.)
303 xDispatcher = queryDispatch(aURL,SPECIALTARGET_SELF,0);
304 }
305 else
306 {
307 css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY );
308 // Normally if isTop() returned sal_False ... the parent frame MUST(!) exist ...
309 // But it seems to be better to check that here to prevent us against an access violation.
310 if (xParent.is())
311 xDispatcher = xParent->queryDispatch(aURL, SPECIALTARGET_TOP, 0);
312 }
313 }
314
315 // I.VI) "_self", ""
316 // Our owner frame should handle this URL. But we can't do it for all of them.
317 // So we ask the internal set controller first. If he disagree we try to find a registered
318 // protocol handler. If this failed too - we check for a loadable content and in case of true
319 // we load it into the frame by returning specialized dispatch object.
320
321 else if (
322 (sTargetFrameName==SPECIALTARGET_SELF) ||
323 (sTargetFrameName.isEmpty())
324 )
325 {
326 // There exist a hard coded interception for special URLs.
327 if ( aURL.Complete == ".uno:CloseDoc" || aURL.Complete == ".uno:CloseWin" )
328 {
329 css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY );
330 // In case the frame is not a top one, is not based on system window and has a parent,
331 // the parent frame should be queried for the correct dispatcher.
332 // See i93473
333 if (
334 !WindowHelper::isTopWindow(xFrame->getContainerWindow()) &&
335 !VCLUnoHelper::GetWindow(xFrame->getContainerWindow())->IsSystemWindow() &&
336 xParent.is()
337 )
338 xDispatcher = xParent->queryDispatch(aURL, SPECIALTARGET_SELF, 0);
339 else
341 }
342 else if ( aURL.Complete == ".uno:CloseFrame" )
344
345 if ( ! xDispatcher.is())
346 {
347 // Ask our controller for his agreement for these dispatched URL ...
348 // because some URLs are internal and can be handled faster by SFX - which most is the current controller!
349 // But in case of e.g. the bibliography not all queries will be handled successfully here.
350 css::uno::Reference< css::frame::XDispatchProvider > xController( xFrame->getController(), css::uno::UNO_QUERY );
351 if (xController.is())
352 xDispatcher = xController->queryDispatch(aURL, SPECIALTARGET_SELF, 0);
353 }
354
355 // If controller has no fun to dispatch these URL - we must search another right dispatcher.
356 // Search for any registered protocol handler first.
357 if (!xDispatcher.is())
358 xDispatcher = implts_searchProtocolHandler(aURL);
359
360 // Not for controller - not for protocol handler
361 // It should be a loadable content - may be a file. Check it ...
362 // This check is necessary to found out, that
363 // support for some protocols isn't installed by user. May be
364 // "ftp" isn't available. So we suppress creation of our self dispatcher.
365 // The result will be clear. He can't handle it - but he would try it.
366 if (
367 ( ! xDispatcher.is() ) &&
369 )
370 {
372 }
373 }
374
375 // I.VII) no further special handlings exist
376 // Now we have to search for the right target frame by calling findFrame() - but should provide our code
377 // against creation of a new task if no frame could be found.
378 // I said it before - it's allowed for dispatch() only.
379
380 else
381 {
382 sal_Int32 nRightFlags = nSearchFlags & ~css::frame::FrameSearchFlag::CREATE;
383
384 // try to find any existing target and ask him for his dispatcher
385 css::uno::Reference< css::frame::XFrame > xFoundFrame = xFrame->findFrame(sTargetFrameName, nRightFlags);
386 if (xFoundFrame.is())
387 {
388 // Attention: Found target is our own owner frame!
389 // Don't ask him for his dispatcher. We know it already - it's our self dispatch helper.
390 // Otherwise we can start a never ending recursiv call. Why?
391 // Somewhere called our owner frame - he called some interceptor objects - and may by this dispatch provider
392 // is called. If wa use queryDispatch() on our owner frame again - we start this call stack again ... and again.
393 if (xFoundFrame==xFrame)
395 else
396 {
397 css::uno::Reference< css::frame::XDispatchProvider > xProvider( xFoundFrame, css::uno::UNO_QUERY );
398 xDispatcher = xProvider->queryDispatch(aURL,SPECIALTARGET_SELF,0);
399 }
400 }
401 else
402 // if it couldn't be found - but creation was allowed
403 // forward request to the desktop.
404 // Note: The given target name must be used to set the name on new created task!
405 // Don't forward request by changing it to a special one e.g _blank.
406 // Use the CREATE flag only to prevent call against further searches.
407 // We already know it - the target must be created new.
408 if (nSearchFlags & css::frame::FrameSearchFlag::CREATE)
409 {
410 css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY );
411 if (xParent.is())
412 xDispatcher = xParent->queryDispatch(aURL, sTargetFrameName, css::frame::FrameSearchFlag::CREATE);
413 }
414 }
415
416 return xDispatcher;
417}
418
433css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_searchProtocolHandler( const css::util::URL& aURL )
434{
435 css::uno::Reference< css::frame::XDispatch > xDispatcher;
436 ProtocolHandler aHandler;
437
438 // This member is threadsafe by himself and lives if we live - we don't need any mutex here.
439 if (m_aProtocolHandlerCache.search(aURL,&aHandler))
440 {
441 css::uno::Reference< css::frame::XDispatchProvider > xHandler;
442 {
444
445 // create it
446 bool bInitialize = true;
447 try
448 {
449 // Only create the protocol handler instance once, the creation is expensive.
450 auto it = m_aProtocolHandlers.find(aHandler.m_sUNOName);
451 if (it == m_aProtocolHandlers.end())
452 {
453 xHandler.set(
454 css::uno::Reference<css::lang::XMultiServiceFactory>(m_xContext->getServiceManager(), css::uno::UNO_QUERY_THROW)
455 ->createInstance(aHandler.m_sUNOName),
456 css::uno::UNO_QUERY);
457
458 // Check if the handler explicitly requested to avoid caching.
459 css::uno::Reference<css::util::XCacheInfo> xCacheInfo(xHandler, css::uno::UNO_QUERY);
460 if (!xCacheInfo.is() || xCacheInfo->isCachingAllowed())
461 {
462 m_aProtocolHandlers.emplace(aHandler.m_sUNOName, xHandler);
463 }
464 }
465 else
466 {
467 xHandler = it->second;
468 bInitialize = false;
469 }
470 }
471 catch(const css::uno::Exception&) {}
472
473 // look if initialization is necessary
474 css::uno::Reference< css::lang::XInitialization > xInit( xHandler, css::uno::UNO_QUERY );
475 if (xInit.is() && bInitialize)
476 {
477 css::uno::Reference< css::frame::XFrame > xOwner( m_xFrame.get(), css::uno::UNO_QUERY );
478 SAL_WARN_IF(!xOwner.is(), "fwk", "DispatchProvider::implts_searchProtocolHandler(): Couldn't get reference to my owner frame. So I can't set may needed context information for this protocol handler.");
479 if (xOwner.is())
480 {
481 try
482 {
483 // but do it only, if all context information is OK
484 css::uno::Sequence< css::uno::Any > lContext{ css::uno::Any(xOwner) };
485 xInit->initialize(lContext);
486 }
487 catch(const css::uno::Exception&) {}
488 }
489 }
490 }
491
492 // ask for his (sub)dispatcher for the given URL
493 if (xHandler.is())
494 xDispatcher = xHandler->queryDispatch(aURL,SPECIALTARGET_SELF,0);
495 }
496
497 return xDispatcher;
498}
499
522css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_getOrCreateDispatchHelper( EDispatchHelper eHelper ,
523 const css::uno::Reference< css::frame::XFrame >& xOwner ,
524 const OUString& sTarget ,
525 sal_Int32 nSearchFlags)
526{
527 css::uno::Reference< css::frame::XDispatch > xDispatchHelper;
528
529 switch (eHelper)
530 {
531 case E_CREATEDISPATCHER :
532 xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, sTarget, nSearchFlags);
533 break;
534
535 case E_BLANKDISPATCHER :
536 {
537 if (xOwner.is())
538 xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, SPECIALTARGET_BLANK, 0);
539 }
540 break;
541
543 {
544 if (xOwner.is())
545 xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, SPECIALTARGET_DEFAULT, 0);
546 }
547 break;
548
549 case E_SELFDISPATCHER :
550 xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, SPECIALTARGET_SELF, 0);
551 break;
552
553 case E_CLOSEDISPATCHER :
554 xDispatchHelper = new CloseDispatcher( m_xContext, xOwner, sTarget );
555 break;
556
558 xDispatchHelper = new StartModuleDispatcher( m_xContext );
559 break;
560 }
561
562 return xDispatchHelper;
563}
564
577bool DispatchProvider::implts_isLoadableContent( const css::util::URL& aURL )
578{
579 LoadEnv::EContentType eType = LoadEnv::classifyContent(aURL.Complete, css::uno::Sequence< css::beans::PropertyValue >());
580 return ( eType == LoadEnv::E_CAN_BE_LOADED );
581}
582
583} // namespace framework
584
585/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
css::uno::Reference< css::lang::XComponent > m_xFrame
static vcl::Window * GetWindow(const css::uno::Reference< css::awt::XWindow > &rxWindow)
helper to dispatch the URLs ".uno:CloseDoc"/".uno:CloseWin"/".uno:CloseFrame" to close a frame/docume...
css::uno::Reference< css::frame::XDispatch > implts_queryDesktopDispatch(const css::uno::Reference< css::frame::XFrame > &xDesktop, const css::util::URL &aURL, const OUString &sTargetFrameName, sal_Int32 nSearchFlags)
helper for queryDispatch() @descr Every member of the frame tree (frame, desktop) must handle such re...
css::uno::Reference< css::frame::XDispatch > implts_getOrCreateDispatchHelper(EDispatchHelper eHelper, const css::uno::Reference< css::frame::XFrame > &xOwner, const OUString &sTarget=OUString(), sal_Int32 nSearchFlags=0)
get or create new dispatch helper @descr Sometimes we need some helper implementations to support dis...
css::uno::Reference< css::frame::XDispatch > implts_searchProtocolHandler(const css::util::URL &aURL)
search for a registered protocol handler and ask him for a dispatch object @descr We search a suitabl...
css::uno::Reference< css::frame::XDispatch > implts_queryFrameDispatch(const css::uno::Reference< css::frame::XFrame > &xFrame, const css::util::URL &aURL, const OUString &sTargetFrameName, sal_Int32 nSearchFlags)
css::uno::WeakReference< css::frame::XFrame > m_xFrame
weakreference to owner frame (Don't use a hard reference. Owner can't delete us then!...
virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches(const css::uno::Sequence< css::frame::DispatchDescriptor > &lDescriptions) override
std::unordered_map< OUString, css::uno::Reference< css::frame::XDispatchProvider > > m_aProtocolHandlers
DispatchProvider(css::uno::Reference< css::uno::XComponentContext > xContext, const css::uno::Reference< css::frame::XFrame > &xFrame)
standard ctor/dtor @descr These initialize a new instance of this class with needed information for w...
HandlerCache m_aProtocolHandlerCache
cache of some other dispatch provider which are registered inside configuration to handle special URL...
bool implts_isLoadableContent(const css::util::URL &aURL)
check URL for support by our used loader or handler @descr If we must return our own dispatch helper ...
virtual ~DispatchProvider() override
protected(!) dtor for deinitializing @descr We made it protected to prevent using of us as base class...
css::uno::Reference< css::uno::XComponentContext > m_xContext
reference to global service manager to create new services
virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch(const css::util::URL &aURL, const OUString &sTargetFrameName, sal_Int32 nSearchFlags) override
bool search(const OUString &sURL, ProtocolHandler *pReturn) const
dtor of the cache @descr It frees all used memory.
implements a dispatch object which can be used to load non-visible components (by using the mechanism...
static EContentType classifyContent(const OUString &sURL, const css::uno::Sequence< css::beans::PropertyValue > &lMediaDescriptor)
checks if the specified content can be handled by a ContentHandler only and is not related to a targe...
Definition: loadenv.cxx:544
EContentType
classify a content.
Definition: loadenv.hxx:87
@ E_CAN_BE_LOADED
identifies a content, which can be loaded into a target frame
Definition: loadenv.hxx:93
helper to handle all URLs related to the StartModule
static bool isTopWindow(const css::uno::Reference< css::awt::XWindow > &xWindow)
Definition: window.hxx:38
bool IsSystemWindow() const
int nCount
URL aURL
DocumentType eType
css::uno::Reference< css::uno::XComponentContext > m_xContext
#define SAL_WARN_IF(condition, area, stream)
EDispatchHelper
@descr We know some special dispatch objects with different functionality.
constexpr OUStringLiteral SPECIALTARGET_BEAMER
Definition: targets.h:33
constexpr OUStringLiteral SPECIALTARGET_TOP
Definition: targets.h:30
constexpr OUStringLiteral SPECIALTARGET_BLANK
Definition: targets.h:31
constexpr OUStringLiteral SPECIALTARGET_DEFAULT
Definition: targets.h:32
constexpr OUStringLiteral SPECIALTARGET_SELF
Definition: targets.h:28
bool isStartModuleDispatch(css::util::URL const &url)
int i
Programmer can register his own services to handle different protocols.
OUString m_sUNOName
the uno implementation name of this handler
Reference< XController > xController
Reference< XFrame > xFrame
#define SPECIALTARGET_PARENT
Definition: targets.h:29