summaryrefslogtreecommitdiffstats
path: root/framework/source/dispatch
diff options
context:
space:
mode:
Diffstat (limited to 'framework/source/dispatch')
-rw-r--r--framework/source/dispatch/closedispatcher.cxx612
-rw-r--r--framework/source/dispatch/dispatchdisabler.cxx170
-rw-r--r--framework/source/dispatch/dispatchinformationprovider.cxx130
-rw-r--r--framework/source/dispatch/dispatchprovider.cxx585
-rw-r--r--framework/source/dispatch/interceptionhelper.cxx270
-rw-r--r--framework/source/dispatch/isstartmoduledispatch.hxx32
-rw-r--r--framework/source/dispatch/loaddispatcher.cxx148
-rw-r--r--framework/source/dispatch/mailtodispatcher.cxx233
-rw-r--r--framework/source/dispatch/oxt_handler.cxx175
-rw-r--r--framework/source/dispatch/popupmenudispatcher.cxx269
-rw-r--r--framework/source/dispatch/servicehandler.cxx260
-rw-r--r--framework/source/dispatch/startmoduledispatcher.cxx147
-rw-r--r--framework/source/dispatch/systemexec.cxx156
-rw-r--r--framework/source/dispatch/windowcommanddispatch.cxx158
14 files changed, 3345 insertions, 0 deletions
diff --git a/framework/source/dispatch/closedispatcher.cxx b/framework/source/dispatch/closedispatcher.cxx
new file mode 100644
index 000000000..a6ff39d45
--- /dev/null
+++ b/framework/source/dispatch/closedispatcher.cxx
@@ -0,0 +1,612 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/closedispatcher.hxx>
+#include <pattern/frame.hxx>
+#include <framework/framelistanalyzer.hxx>
+#include <services.h>
+
+#include <com/sun/star/bridge/BridgeFactory.hpp>
+#include <com/sun/star/bridge/XBridgeFactory2.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/CommandGroup.hpp>
+#include <com/sun/star/frame/StartModule.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/document/XActionLockable.hpp>
+#include <com/sun/star/beans/XFastPropertySet.hpp>
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <osl/diagnose.h>
+#include <utility>
+#include <vcl/window.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/syswin.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace com::sun::star;
+
+namespace framework{
+
+#ifdef fpf
+ #error "Who uses \"fpf\" as define. It will overwrite my namespace alias ..."
+#endif
+namespace fpf = ::framework::pattern::frame;
+
+constexpr OUStringLiteral URL_CLOSEDOC = u".uno:CloseDoc";
+constexpr OUStringLiteral URL_CLOSEWIN = u".uno:CloseWin";
+const char URL_CLOSEFRAME[] = ".uno:CloseFrame";
+
+CloseDispatcher::CloseDispatcher(css::uno::Reference< css::uno::XComponentContext > xContext ,
+ const css::uno::Reference< css::frame::XFrame >& xFrame ,
+ std::u16string_view sTarget)
+ : m_xContext(std::move(xContext))
+ , m_aAsyncCallback(
+ new vcl::EventPoster(LINK(this, CloseDispatcher, impl_asyncCallback)))
+ , m_eOperation(E_CLOSE_DOC)
+ , m_pSysWindow(nullptr)
+{
+ uno::Reference<frame::XFrame> xTarget = static_impl_searchRightTargetFrame(xFrame, sTarget);
+ m_xCloseFrame = xTarget;
+
+ // Try to retrieve the system window instance of the closing frame.
+ uno::Reference<awt::XWindow> xWindow = xTarget->getContainerWindow();
+ if (xWindow.is())
+ {
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow);
+ if (pWindow->IsSystemWindow())
+ m_pSysWindow = dynamic_cast<SystemWindow*>(pWindow.get());
+ }
+}
+
+CloseDispatcher::~CloseDispatcher()
+{
+ SolarMutexGuard g;
+ m_aAsyncCallback.reset();
+ m_pSysWindow.reset();
+}
+
+void SAL_CALL CloseDispatcher::dispatch(const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments)
+{
+ dispatchWithNotification(aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >());
+}
+
+css::uno::Sequence< sal_Int16 > SAL_CALL CloseDispatcher::getSupportedCommandGroups()
+{
+ return css::uno::Sequence< sal_Int16 >{css::frame::CommandGroup::VIEW, css::frame::CommandGroup::DOCUMENT};
+}
+
+css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL CloseDispatcher::getConfigurableDispatchInformation(sal_Int16 nCommandGroup)
+{
+ if (nCommandGroup == css::frame::CommandGroup::VIEW)
+ {
+ /* Attention: Don't add .uno:CloseFrame here. Because it's not really
+ a configurable feature ... and further it does not have
+ a valid UIName entry inside the GenericCommands.xcu ... */
+ css::uno::Sequence< css::frame::DispatchInformation > lViewInfos{
+ { URL_CLOSEWIN, css::frame::CommandGroup::VIEW }
+ };
+ return lViewInfos;
+ }
+ else if (nCommandGroup == css::frame::CommandGroup::DOCUMENT)
+ {
+ css::uno::Sequence< css::frame::DispatchInformation > lDocInfos{
+ { URL_CLOSEDOC, css::frame::CommandGroup::DOCUMENT }
+ };
+ return lDocInfos;
+ }
+
+ return css::uno::Sequence< css::frame::DispatchInformation >();
+}
+
+void SAL_CALL CloseDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/,
+ const css::util::URL& /*aURL*/ )
+{
+}
+
+void SAL_CALL CloseDispatcher::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/,
+ const css::util::URL& /*aURL*/ )
+{
+}
+
+void SAL_CALL CloseDispatcher::dispatchWithNotification(const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ // SAFE -> ----------------------------------
+ SolarMutexClearableGuard aWriteLock;
+
+ // This reference indicates, that we were already called before and
+ // our asynchronous process was not finished yet.
+ // We have to reject double calls. Otherwise we risk,
+ // that we try to close an already closed resource...
+ // And it is no problem to do nothing then. The UI user will try it again, if
+ // non of these jobs was successful.
+ if (m_xSelfHold.is())
+ {
+ aWriteLock.clear();
+ // <- SAFE ------------------------------
+
+ implts_notifyResultListener(
+ xListener,
+ css::frame::DispatchResultState::DONTKNOW,
+ css::uno::Any());
+ return;
+ }
+
+ // First we have to check, if this dispatcher is used right. Means if valid URLs are used.
+ // If not - we have to break this operation. But an optional listener must be informed.
+ // BTW: We save the information about the requested operation. Because
+ // we need it later.
+ if ( aURL.Complete == URL_CLOSEDOC )
+ m_eOperation = E_CLOSE_DOC;
+ else if ( aURL.Complete == URL_CLOSEWIN )
+ m_eOperation = E_CLOSE_WIN;
+ else if ( aURL.Complete == URL_CLOSEFRAME )
+ m_eOperation = E_CLOSE_FRAME;
+ else
+ {
+ aWriteLock.clear();
+ // <- SAFE ------------------------------
+
+ implts_notifyResultListener(
+ xListener,
+ css::frame::DispatchResultState::FAILURE,
+ css::uno::Any());
+ return;
+ }
+
+ if (m_pSysWindow && m_pSysWindow->GetCloseHdl().IsSet())
+ {
+ // The closing frame has its own close handler. Call it instead.
+ m_pSysWindow->GetCloseHdl().Call(*m_pSysWindow);
+
+ aWriteLock.clear();
+ // <- SAFE ------------------------------
+
+ implts_notifyResultListener(
+ xListener,
+ css::frame::DispatchResultState::SUCCESS,
+ css::uno::Any());
+
+ return;
+ }
+
+ // OK - URLs are the right ones.
+ // But we can't execute synchronously :-)
+ // May we are called from a generic key-input handler,
+ // which isn't aware that this call kill its own environment...
+ // Do it asynchronous everytimes!
+
+ // But don't forget to hold ourselves alive.
+ // We are called back from an environment, which doesn't know a uno reference.
+ // They call us back by using our c++ interface.
+
+ m_xResultListener = xListener;
+ m_xSelfHold.set(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
+
+ aWriteLock.clear();
+ // <- SAFE ----------------------------------
+
+ bool bIsSynchron = false;
+ for (const css::beans::PropertyValue& rArg : lArguments )
+ {
+ if ( rArg.Name == "SynchronMode" )
+ {
+ rArg.Value >>= bIsSynchron;
+ break;
+ }
+ }
+
+ if ( bIsSynchron )
+ impl_asyncCallback(nullptr);
+ else
+ {
+ SolarMutexGuard g;
+ m_aAsyncCallback->Post();
+ }
+}
+
+/**
+ @short asynchronous callback
+ @descr We start all actions inside this object asynchronous
+ (see comments there).
+ Now we do the following:
+ - close all views to the same document, if needed and possible
+ - make the current frame empty
+ ! This step is necessary to handle errors during closing the
+ document inside the frame. May the document shows a dialog and
+ the user ignore it. Then the state of the office can be changed
+ during we try to close frame and document.
+ - check the environment (means count open frames - excluding our
+ current one)
+ - decide then, if we must close this frame only, establish the backing mode
+ or shutdown the whole application.
+*/
+IMPL_LINK_NOARG(CloseDispatcher, impl_asyncCallback, LinkParamNone*, void)
+{
+ try
+ {
+
+ // Allow calling of XController->suspend() everytimes.
+ // Dispatch is an UI functionality. We implement such dispatch object here.
+ // And further XController->suspend() was designed to bring an UI ...
+ bool bControllerSuspended = false;
+
+ bool bCloseAllViewsToo;
+ EOperation eOperation;
+ css::uno::Reference< css::uno::XComponentContext > xContext;
+ css::uno::Reference< css::frame::XFrame > xCloseFrame;
+ css::uno::Reference< css::frame::XDispatchResultListener > xListener;
+ {
+ SolarMutexGuard g;
+
+ // Closing of all views, related to the same document, is allowed
+ // only if the dispatched URL was ".uno:CloseDoc"!
+ bCloseAllViewsToo = (m_eOperation == E_CLOSE_DOC);
+
+ eOperation = m_eOperation;
+ xContext = m_xContext;
+ xCloseFrame.set(m_xCloseFrame.get(), css::uno::UNO_QUERY);
+ xListener = m_xResultListener;
+ }
+
+ // frame already dead ?!
+ // Nothing to do !
+ if (! xCloseFrame.is())
+ return;
+
+ bool bCloseFrame = false;
+ bool bEstablishBackingMode = false;
+ bool bTerminateApp = false;
+
+ // Analyze the environment a first time.
+ // If we found some special cases, we can
+ // make some decisions earlier!
+ css::uno::Reference< css::frame::XFramesSupplier > xDesktop( css::frame::Desktop::create(xContext), css::uno::UNO_QUERY_THROW);
+ FrameListAnalyzer aCheck1(xDesktop, xCloseFrame, FrameAnalyzerFlags::Help | FrameAnalyzerFlags::BackingComponent);
+
+ // Check for existing UNO connections.
+ // NOTE: There is a race between checking this and connections being created/destroyed before
+ // we close the frame / terminate the app.
+ css::uno::Reference<css::bridge::XBridgeFactory2> bridgeFac( css::bridge::BridgeFactory::create(xContext) );
+ bool bHasActiveConnections = bridgeFac->getExistingBridges().hasElements();
+
+ // a) If the current frame (where the close dispatch was requested for) does not have
+ // any parent frame ... it will close this frame only. Such frame isn't part of the
+ // global desktop tree ... and such frames are used as "implementation details" only.
+ // E.g. the live previews of our wizards doing such things. And then the owner of the frame
+ // is responsible for closing the application or accepting closing of the application
+ // by others.
+ if ( ! xCloseFrame->getCreator().is())
+ bCloseFrame = true;
+
+ // b) The help window can't disagree with any request.
+ // Because it doesn't implement a controller - it uses a window only.
+ // Further it can't be the last open frame - if we do all other things
+ // right inside this CloseDispatcher implementation.
+ // => close it!
+ else if (aCheck1.m_bReferenceIsHelp)
+ bCloseFrame = true;
+
+ // c) If we are already in "backing mode", we terminate the application, if no active UNO connections are found.
+ // If there is an active UNO connection, we only close the frame and leave the application alive.
+ // It doesn't matter, how many other frames (can be the help or hidden frames only) are open then.
+ else if (aCheck1.m_bReferenceIsBacking) {
+ if (bHasActiveConnections)
+ bCloseFrame = true;
+ else
+ bTerminateApp = true;
+ }
+
+ // d) Otherwise we have to: close all views to the same document, close the
+ // document inside our own frame and decide then again, what has to be done!
+ else
+ {
+ if (implts_prepareFrameForClosing(m_xCloseFrame, bCloseAllViewsToo, bControllerSuspended))
+ {
+ // OK; this frame is empty now.
+ // Check the environment again to decide, what is the next step.
+ FrameListAnalyzer aCheck2(xDesktop, xCloseFrame, FrameAnalyzerFlags::All);
+
+ // c1) there is as minimum 1 frame open, which is visible and contains a document
+ // different from our one. And it's not the help!
+ // => close our frame only - nothing else.
+ if (!aCheck2.m_lOtherVisibleFrames.empty())
+ bCloseFrame = true;
+ else
+
+ // c2) if we close the current view ... but not all other views
+ // to the same document, we must close the current frame only!
+ // Because implts_closeView() suspended this view only - does not
+ // close the frame.
+ if (
+ (!bCloseAllViewsToo ) &&
+ (!aCheck2.m_lModelFrames.empty())
+ )
+ bCloseFrame = true;
+
+ else
+ // c3) there is no other (visible) frame open ...
+ // The help module will be ignored everytimes!
+ // But we have to decide if we must terminate the
+ // application or establish the backing mode now.
+ // And that depends from the dispatched URL ...
+ {
+ if (eOperation == E_CLOSE_FRAME)
+ {
+ if (bHasActiveConnections)
+ bCloseFrame = true;
+ else
+ bTerminateApp = true;
+ }
+ else if( SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE) )
+ bEstablishBackingMode = true;
+ else if (bHasActiveConnections)
+ bCloseFrame = true;
+ else
+ bTerminateApp = true;
+ }
+ }
+ }
+
+ // Do it now ...
+ bool bSuccess = false;
+ if (bCloseFrame)
+ bSuccess = implts_closeFrame();
+ else if (bEstablishBackingMode)
+ #if defined MACOSX
+ {
+ // on mac close down, quickstarter keeps the process alive
+ // however if someone has shut down the quickstarter
+ // behave as any other platform
+
+ bool bQuickstarterRunning = false;
+ // get quickstart service
+ try
+ {
+ css::uno::Reference< css::beans::XFastPropertySet > xSet( xContext->getServiceManager()->createInstanceWithContext(IMPLEMENTATIONNAME_QUICKLAUNCHER, xContext), css::uno::UNO_QUERY_THROW );
+ css::uno::Any aVal( xSet->getFastPropertyValue( 0 ) );
+ bool bState = false;
+ if( aVal >>= bState )
+ bQuickstarterRunning = bState;
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+ bSuccess = bQuickstarterRunning ? implts_terminateApplication() : implts_establishBackingMode();
+ }
+ #else
+ bSuccess = implts_establishBackingMode();
+ #endif
+ else if (bTerminateApp)
+ bSuccess = implts_terminateApplication();
+
+ if ( ! bSuccess && bControllerSuspended )
+ {
+ css::uno::Reference< css::frame::XController > xController = xCloseFrame->getController();
+ if (xController.is())
+ xController->suspend(false);
+ }
+
+ // inform listener
+ sal_Int16 nState = css::frame::DispatchResultState::FAILURE;
+ if (bSuccess)
+ nState = css::frame::DispatchResultState::SUCCESS;
+ implts_notifyResultListener(xListener, nState, css::uno::Any());
+
+ SolarMutexGuard g;
+ // This method was called asynchronous from our main thread by using a pointer.
+ // We reached this method only, by using a reference to ourself :-)
+ // Further this member is used to detect still running and not yet finished
+ // asynchronous operations. So it's time now to release this reference.
+ // But hold it temp alive. Otherwise we die before we can finish this method really :-))
+ css::uno::Reference< css::uno::XInterface > xTempHold = m_xSelfHold;
+ m_xSelfHold.clear();
+ m_xResultListener.clear();
+ }
+ catch(const css::lang::DisposedException&)
+ {
+ }
+}
+
+bool CloseDispatcher::implts_prepareFrameForClosing(const css::uno::Reference< css::frame::XFrame >& xFrame,
+ bool bCloseAllOtherViewsToo,
+ bool& bControllerSuspended )
+{
+ // Frame already dead ... so this view is closed ... is closed ... is ... .-)
+ if (! xFrame.is())
+ return true;
+
+ // Close all views to the same document ... if forced to do so.
+ // But don't touch our own frame here!
+ // We must do so ... because the may be following controller->suspend()
+ // will show the "save/discard/cancel" dialog for the last view only!
+ if (bCloseAllOtherViewsToo)
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext;
+ {
+ SolarMutexGuard g;
+ xContext = m_xContext;
+ }
+
+ css::uno::Reference< css::frame::XFramesSupplier > xDesktop( css::frame::Desktop::create( xContext ), css::uno::UNO_QUERY_THROW);
+ FrameListAnalyzer aCheck(xDesktop, xFrame, FrameAnalyzerFlags::All);
+
+ size_t c = aCheck.m_lModelFrames.size();
+ size_t i = 0;
+ for (i=0; i<c; ++i)
+ {
+ if (!fpf::closeIt(aCheck.m_lModelFrames[i]))
+ return false;
+ }
+ }
+
+ // Inform user about modified documents or still running jobs (e.g. printing).
+ {
+ css::uno::Reference< css::frame::XController > xController = xFrame->getController();
+ if (xController.is()) // some views don't uses a controller .-( (e.g. the help window)
+ {
+ bControllerSuspended = xController->suspend(true);
+ if (! bControllerSuspended)
+ return false;
+ }
+ }
+
+ // don't remove the component really by e.g. calling setComponent(null, null).
+ // It's enough to suspend the controller.
+ // If we close the frame later this controller doesn't show the same dialog again.
+ return true;
+}
+
+bool CloseDispatcher::implts_closeFrame()
+{
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ {
+ SolarMutexGuard g;
+ xFrame.set(m_xCloseFrame.get(), css::uno::UNO_QUERY);
+ }
+
+ // frame already dead ? => so it's closed ... it's closed ...
+ if ( ! xFrame.is() )
+ return true;
+
+ // don't deliver ownership; our "UI user" will try it again if it failed.
+ // OK - he will get an empty frame then. But normally an empty frame
+ // should be closeable always :-)
+ if (!fpf::closeIt(xFrame))
+ return false;
+
+ {
+ SolarMutexGuard g;
+ m_xCloseFrame.clear();
+ }
+
+ return true;
+}
+
+bool CloseDispatcher::implts_establishBackingMode()
+{
+ css::uno::Reference< css::uno::XComponentContext > xContext;
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ {
+ SolarMutexGuard g;
+ xContext = m_xContext;
+ xFrame.set(m_xCloseFrame.get(), css::uno::UNO_QUERY);
+ }
+
+ if (!xFrame.is())
+ return false;
+
+ css::uno::Reference < css::document::XActionLockable > xLock( xFrame, css::uno::UNO_QUERY );
+ if ( xLock.is() && xLock->isActionLocked() )
+ return false;
+
+ css::uno::Reference< css::awt::XWindow > xContainerWindow = xFrame->getContainerWindow();
+
+ css::uno::Reference< css::frame::XController > xStartModule = css::frame::StartModule::createWithParentWindow(
+ xContext, xContainerWindow);
+
+ // Attention: You MUST(!) call setComponent() before you call attachFrame().
+ css::uno::Reference< css::awt::XWindow > xBackingWin(xStartModule, css::uno::UNO_QUERY);
+ xFrame->setComponent(xBackingWin, xStartModule);
+ xStartModule->attachFrame(xFrame);
+ xContainerWindow->setVisible(true);
+
+ return true;
+}
+
+bool CloseDispatcher::implts_terminateApplication()
+{
+ css::uno::Reference< css::uno::XComponentContext > xContext;
+ {
+ SolarMutexGuard g;
+ xContext = m_xContext;
+ }
+
+ css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext );
+
+ return xDesktop->terminate();
+}
+
+void CloseDispatcher::implts_notifyResultListener(const css::uno::Reference< css::frame::XDispatchResultListener >& xListener,
+ sal_Int16 nState ,
+ const css::uno::Any& aResult )
+{
+ if (!xListener.is())
+ return;
+
+ css::frame::DispatchResultEvent aEvent(
+ css::uno::Reference< css::uno::XInterface >(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY),
+ nState,
+ aResult);
+
+ xListener->dispatchFinished(aEvent);
+}
+
+css::uno::Reference< css::frame::XFrame > CloseDispatcher::static_impl_searchRightTargetFrame(const css::uno::Reference< css::frame::XFrame >& xFrame ,
+ std::u16string_view sTarget)
+{
+ if (o3tl::equalsIgnoreAsciiCase(sTarget, u"_self"))
+ return xFrame;
+
+ OSL_ENSURE(sTarget.empty(), "CloseDispatch used for unexpected target. Magic things will happen now .-)");
+
+ css::uno::Reference< css::frame::XFrame > xTarget = xFrame;
+ while(true)
+ {
+ // a) top frames will be closed
+ if (xTarget->isTop())
+ return xTarget;
+
+ // b) even child frame containing top level windows (e.g. query designer of database) will be closed
+ css::uno::Reference< css::awt::XWindow > xWindow = xTarget->getContainerWindow();
+ css::uno::Reference< css::awt::XTopWindow > xTopWindowCheck(xWindow, css::uno::UNO_QUERY);
+ if (xTopWindowCheck.is())
+ {
+ // b1) Note: Toolkit interface XTopWindow sometimes is used by real VCL-child-windows also .-)
+ // Be sure that these window is really a "top system window".
+ // Attention ! Checking Window->GetParent() isn't the right approach here.
+ // Because sometimes VCL create "implicit border windows" as parents even we created
+ // a simple XWindow using the toolkit only .-(
+ SolarMutexGuard aSolarLock;
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ if ( pWindow && pWindow->IsSystemWindow() )
+ return xTarget;
+ }
+
+ // c) try to find better results on parent frame
+ // If no parent frame exists (because this frame is used outside the desktop tree)
+ // the given frame must be used directly.
+ css::uno::Reference< css::frame::XFrame > xParent = xTarget->getCreator();
+ if ( ! xParent.is())
+ return xTarget;
+
+ // c1) check parent frame inside next loop ...
+ xTarget = xParent;
+ }
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/dispatchdisabler.cxx b/framework/source/dispatch/dispatchdisabler.cxx
new file mode 100644
index 000000000..2341d0dd6
--- /dev/null
+++ b/framework/source/dispatch/dispatchdisabler.cxx
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <services.h>
+#include <dispatch/dispatchdisabler.hxx>
+
+#include <com/sun/star/frame/DispatchDescriptor.hpp>
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace css;
+using namespace framework;
+
+DispatchDisabler::DispatchDisabler(const uno::Reference< uno::XComponentContext >& )
+{
+}
+
+// XInitialization
+void SAL_CALL DispatchDisabler::initialize( const uno::Sequence< uno::Any >& aArguments )
+{
+ uno::Sequence< OUString > aDisabledURLs;
+ if( aArguments.hasElements() &&
+ ( aArguments[0] >>= aDisabledURLs ) )
+ {
+ for( OUString const & url : std::as_const(aDisabledURLs) )
+ maDisabledURLs.insert(url);
+ }
+}
+
+// XDispatchProvider
+uno::Reference< frame::XDispatch > SAL_CALL
+DispatchDisabler::queryDispatch( const util::URL& rURL,
+ const OUString& rTargetFrameName,
+ ::sal_Int32 nSearchFlags )
+{
+ // If present - disabled.
+ if( maDisabledURLs.find(rURL.Complete) != maDisabledURLs.end() ||
+ !mxSlave.is() )
+ return uno::Reference< frame::XDispatch >();
+ else
+ return mxSlave->queryDispatch(rURL, rTargetFrameName, nSearchFlags);
+}
+
+uno::Sequence< uno::Reference< frame::XDispatch > > SAL_CALL
+DispatchDisabler::queryDispatches( const uno::Sequence< frame::DispatchDescriptor >& rRequests )
+{
+ uno::Sequence< uno::Reference< frame::XDispatch > > aResult(rRequests.getLength());
+ auto aResultRange = asNonConstRange(aResult);
+ for( sal_Int32 i = 0; i < rRequests.getLength(); ++i )
+ aResultRange[i] = queryDispatch(rRequests[i].FeatureURL,
+ rRequests[i].FrameName,
+ rRequests[i].SearchFlags);
+ return aResult;
+}
+
+// XDispatchProviderInterceptor
+uno::Reference< frame::XDispatchProvider > SAL_CALL
+DispatchDisabler::getSlaveDispatchProvider()
+{
+ return mxSlave;
+}
+
+void SAL_CALL DispatchDisabler::setSlaveDispatchProvider( const uno::Reference< frame::XDispatchProvider >& xNewDispatchProvider )
+{
+ mxSlave = xNewDispatchProvider;
+}
+
+uno::Reference< frame::XDispatchProvider > SAL_CALL
+DispatchDisabler::getMasterDispatchProvider()
+{
+ return mxMaster;
+}
+void SAL_CALL
+DispatchDisabler::setMasterDispatchProvider( const uno::Reference< frame::XDispatchProvider >& xNewSupplier )
+{
+ mxMaster = xNewSupplier;
+}
+
+// XInterceptorInfo
+uno::Sequence< OUString > SAL_CALL
+ DispatchDisabler::getInterceptedURLs()
+{
+ uno::Sequence< OUString > aDisabledURLs(maDisabledURLs.size());
+ auto aDisabledURLsRange = asNonConstRange(aDisabledURLs);
+ sal_Int32 n = 0;
+ for (auto const& disabledURL : maDisabledURLs)
+ aDisabledURLsRange[n++] = disabledURL;
+ return aDisabledURLs;
+}
+
+// XElementAccess
+uno::Type SAL_CALL DispatchDisabler::getElementType()
+{
+ uno::Type aModuleType = cppu::UnoType<OUString>::get();
+ return aModuleType;
+}
+
+::sal_Bool SAL_CALL DispatchDisabler::hasElements()
+{
+ return !maDisabledURLs.empty();
+}
+
+// XNameAccess
+uno::Any SAL_CALL DispatchDisabler::getByName( const OUString& )
+{
+ return uno::Any();
+}
+
+uno::Sequence< OUString > SAL_CALL DispatchDisabler::getElementNames()
+{
+ return getInterceptedURLs();
+}
+
+sal_Bool SAL_CALL DispatchDisabler::hasByName( const OUString& rName )
+{
+ return maDisabledURLs.find(rName) != maDisabledURLs.end();
+}
+
+// XNameReplace
+void SAL_CALL DispatchDisabler::replaceByName( const OUString& rName, const uno::Any& aElement )
+{
+ removeByName( rName );
+ insertByName( rName, aElement );
+}
+
+// XNameContainer
+void DispatchDisabler::insertByName( const OUString& rName, const uno::Any& )
+{
+ maDisabledURLs.insert(rName);
+}
+
+void DispatchDisabler::removeByName( const OUString& rName )
+{
+ auto it = maDisabledURLs.find(rName);
+ if( it != maDisabledURLs.end() )
+ maDisabledURLs.erase(it);
+}
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL DispatchDisabler::getImplementationName()
+{
+ return "com.sun.star.comp.framework.services.DispatchDisabler";
+}
+
+sal_Bool SAL_CALL DispatchDisabler::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL DispatchDisabler::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.DispatchDisabler" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_DispatchDisabler_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::DispatchDisabler(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/dispatchinformationprovider.cxx b/framework/source/dispatch/dispatchinformationprovider.cxx
new file mode 100644
index 000000000..a5a0bd698
--- /dev/null
+++ b/framework/source/dispatch/dispatchinformationprovider.cxx
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/dispatchinformationprovider.hxx>
+#include <dispatch/closedispatcher.hxx>
+
+#include <com/sun/star/frame/AppDispatchProvider.hpp>
+
+#include <comphelper/sequence.hxx>
+
+#include <unordered_map>
+#include <utility>
+
+namespace framework{
+
+DispatchInformationProvider::DispatchInformationProvider(css::uno::Reference< css::uno::XComponentContext > xContext ,
+ const css::uno::Reference< css::frame::XFrame >& xFrame)
+ : m_xContext (std::move(xContext ))
+ , m_xFrame (xFrame )
+{
+}
+
+DispatchInformationProvider::~DispatchInformationProvider()
+{
+}
+
+css::uno::Sequence< sal_Int16 > SAL_CALL DispatchInformationProvider::getSupportedCommandGroups()
+{
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > > lProvider = implts_getAllSubProvider();
+ sal_Int32 c1 = lProvider.getLength();
+ sal_Int32 i1 = 0;
+
+ ::std::vector< sal_Int16 > lGroups;
+
+ for (i1=0; i1<c1; ++i1)
+ {
+ // ignore controller, which doesn't implement the right interface
+ css::uno::Reference< css::frame::XDispatchInformationProvider > xProvider = lProvider[i1];
+ if (!xProvider.is())
+ continue;
+
+ const css::uno::Sequence< sal_Int16 > lProviderGroups = xProvider->getSupportedCommandGroups();
+ sal_Int32 c2 = lProviderGroups.getLength();
+ sal_Int32 i2 = 0;
+ for (i2=0; i2<c2; ++i2)
+ {
+ const sal_Int16& rGroup = lProviderGroups[i2];
+ ::std::vector< sal_Int16 >::const_iterator pGroup =
+ ::std::find(lGroups.begin(), lGroups.end(), rGroup);
+ if (pGroup == lGroups.end())
+ lGroups.push_back(rGroup);
+ }
+ }
+
+ return ::comphelper::containerToSequence(lGroups);
+}
+
+css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL DispatchInformationProvider::getConfigurableDispatchInformation(sal_Int16 nCommandGroup)
+{
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > > lProvider = implts_getAllSubProvider();
+ sal_Int32 c1 = lProvider.getLength();
+ sal_Int32 i1 = 0;
+
+ std::unordered_map<OUString, css::frame::DispatchInformation> lInfos;
+
+ for (i1=0; i1<c1; ++i1)
+ {
+ try
+ {
+ // ignore controller, which doesn't implement the right interface
+ css::uno::Reference< css::frame::XDispatchInformationProvider > xProvider = lProvider[i1];
+ if (!xProvider.is())
+ continue;
+
+ const css::uno::Sequence< css::frame::DispatchInformation > lProviderInfos = xProvider->getConfigurableDispatchInformation(nCommandGroup);
+ sal_Int32 c2 = lProviderInfos.getLength();
+ sal_Int32 i2 = 0;
+ for (i2=0; i2<c2; ++i2)
+ {
+ const css::frame::DispatchInformation& rInfo = lProviderInfos[i2];
+ auto pInfo = lInfos.find(rInfo.Command);
+ if (pInfo == lInfos.end())
+ lInfos[rInfo.Command] = rInfo;
+ }
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { continue; }
+ }
+
+ return comphelper::mapValuesToSequence(lInfos);
+}
+
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > > DispatchInformationProvider::implts_getAllSubProvider()
+{
+ css::uno::Reference< css::frame::XFrame > xFrame(m_xFrame);
+ if (!xFrame.is())
+ return css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > >();
+
+ rtl::Reference<CloseDispatcher> xCloser = new CloseDispatcher(m_xContext, xFrame, u"_self"); // explicit "_self" ... not "" ... see implementation of close dispatcher itself!
+
+ css::uno::Reference< css::frame::XDispatchInformationProvider > xController (xFrame->getController() , css::uno::UNO_QUERY);
+ css::uno::Reference< css::frame::XDispatchInformationProvider > xAppDispatcher = css::frame::AppDispatchProvider::create(m_xContext);
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatchInformationProvider > > lProvider{
+ xController, xCloser, xAppDispatcher
+ };
+
+ return lProvider;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/dispatchprovider.cxx b/framework/source/dispatch/dispatchprovider.cxx
new file mode 100644
index 000000000..8328e9c42
--- /dev/null
+++ b/framework/source/dispatch/dispatchprovider.cxx
@@ -0,0 +1,585 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/dispatchprovider.hxx>
+#include <loadenv/loadenv.hxx>
+#include <dispatch/loaddispatcher.hxx>
+#include <dispatch/closedispatcher.hxx>
+#include <dispatch/startmoduledispatcher.hxx>
+
+#include <pattern/window.hxx>
+#include <targets.h>
+#include "isstartmoduledispatch.hxx"
+
+#include <com/sun/star/frame/XDesktop.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/util/XCacheInfo.hpp>
+
+#include <rtl/ustring.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+#include <framework/dispatchhelper.hxx>
+
+namespace framework{
+
+/**
+ @short standard ctor/dtor
+ @descr These initialize a new instance of this class with needed information for work.
+ We hold a weakreference to our owner frame which start dispatches at us.
+ We can't use a normal reference because he hold a reference of us too ...
+ nobody can die so ...!
+
+ @seealso using at owner
+
+ @param rxContext
+ reference to servicemanager to create new services.
+ @param xFrame
+ reference to our owner frame.
+*/
+DispatchProvider::DispatchProvider( css::uno::Reference< css::uno::XComponentContext > xContext ,
+ const css::uno::Reference< css::frame::XFrame >& xFrame )
+ : m_xContext (std::move( xContext ))
+ , m_xFrame ( xFrame )
+{
+}
+
+/**
+ @short protected(!) dtor for deinitializing
+ @descr We made it protected to prevent using of us as base class instead as a member.
+ */
+DispatchProvider::~DispatchProvider()
+{
+}
+
+/**
+ @interface XDispatchProvider
+ @short search a dispatcher for given URL
+ @descr If no interceptor is set on owner, we search for right frame and dispatch URL to it.
+ If no frame was found, we do nothing.
+ But we don't do it directly here. We detect the type of our owner frame and calls
+ specialized queryDispatch() helper dependen from that. Because a Desktop handle some
+ requests in another way then a normal frame.
+
+ @param aURL
+ URL to dispatch.
+ @param sTargetFrameName
+ name of searched frame.
+ @param nSearchFlags
+ flags for searching.
+ @return A reference to a dispatch object for this URL (if someone was found!).
+
+ @threadsafe yes
+*/
+css::uno::Reference< css::frame::XDispatch > SAL_CALL DispatchProvider::queryDispatch( const css::util::URL& aURL ,
+ const OUString& sTargetFrameName ,
+ sal_Int32 nSearchFlags )
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatcher;
+
+ css::uno::Reference< css::frame::XFrame > xOwner(m_xFrame);
+
+ css::uno::Reference< css::frame::XDesktop > xDesktopCheck( xOwner, css::uno::UNO_QUERY );
+
+ if (xDesktopCheck.is())
+ xDispatcher = implts_queryDesktopDispatch(xOwner, aURL, sTargetFrameName, nSearchFlags);
+ else
+ xDispatcher = implts_queryFrameDispatch(xOwner, aURL, sTargetFrameName, nSearchFlags);
+
+ return xDispatcher;
+}
+
+/**
+ @interface XDispatchProvider
+ @short do the same like queryDispatch() ... but handle multiple dispatches at the same time
+ @descr It's an optimism. User give us a list of queries ... and we return a list of dispatcher.
+ If one of given queries couldn't be solved to a real existing dispatcher ...
+ we return a list with empty references in it! Order of both lists will be retained!
+
+ @seealso method queryDispatch()
+
+ @param lDescriptions
+ a list of all dispatch parameters for multiple requests
+ @return A reference a list of dispatch objects for these URLs - may with some <NULL/> values inside.
+
+ @threadsafe yes
+*/
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL DispatchProvider::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptions )
+{
+ // Create return list - which must have same size then the given descriptor
+ // It's not allowed to pack it!
+ sal_Int32 nCount = lDescriptions.getLength();
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount );
+ auto lDispatcherRange = asNonConstRange(lDispatcher);
+ // Step over all descriptors and try to get any dispatcher for it.
+ for( sal_Int32 i=0; i<nCount; ++i )
+ {
+ lDispatcherRange[i] = queryDispatch( lDescriptions[i].FeatureURL ,
+ lDescriptions[i].FrameName ,
+ lDescriptions[i].SearchFlags );
+ }
+
+ return lDispatcher;
+}
+
+/**
+ @short helper for queryDispatch()
+ @descr Every member of the frame tree (frame, desktop) must handle such request
+ in another way. So we implement different specialized methods for everyone.
+
+ @threadsafe yes
+ */
+css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_queryDesktopDispatch( const css::uno::Reference< css::frame::XFrame >& xDesktop ,
+ const css::util::URL& aURL ,
+ const OUString& sTargetFrameName ,
+ sal_Int32 nSearchFlags )
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatcher;
+
+ // ignore wrong requests which are not supported
+ if (
+ (sTargetFrameName==SPECIALTARGET_PARENT ) || // we have no parent by definition
+ (sTargetFrameName==SPECIALTARGET_BEAMER ) // beamer frames are allowed as child of tasks only -
+ // and they exist more than ones. We have no idea which our sub tasks is the right one
+ )
+ {
+ return nullptr;
+ }
+
+ // I) handle special cases which not right for using findFrame() first
+
+ // I.I) "_blank"
+ // It's not the right place to create a new task here - because we are queried for a dispatch object
+ // only, which can handle such request. Such dispatcher should create the required task on demand.
+ // Normally the functionality for "_blank" is provided by findFrame() - but that would create it directly
+ // here. that's why we must "intercept" here.
+
+ if (sTargetFrameName==SPECIALTARGET_BLANK)
+ {
+ if (implts_isLoadableContent(aURL))
+ xDispatcher = implts_getOrCreateDispatchHelper( E_BLANKDISPATCHER, xDesktop );
+ }
+
+ // I.II) "_default"
+ // This is a combination of search an empty task for recycling - or create a new one.
+
+ else if (sTargetFrameName==SPECIALTARGET_DEFAULT)
+ {
+ if (implts_isLoadableContent(aURL))
+ xDispatcher = implts_getOrCreateDispatchHelper( E_DEFAULTDISPATCHER, xDesktop );
+
+ if (isStartModuleDispatch(aURL))
+ xDispatcher = implts_getOrCreateDispatchHelper( E_STARTMODULEDISPATCHER, xDesktop );
+ }
+
+ // I.III) "_self", "", "_top"
+ // The desktop can't load any document - but he can handle some special protocols like "uno", "slot" ...
+ // Why is "top" here handled too? Because the desktop is the topest frame. Normally it's superfluous
+ // to use this target - but we can handle it in the same manner then "_self".
+
+ else if (
+ (sTargetFrameName==SPECIALTARGET_SELF) ||
+ (sTargetFrameName==SPECIALTARGET_TOP ) ||
+ (sTargetFrameName.isEmpty())
+ )
+ {
+ xDispatcher = implts_searchProtocolHandler(aURL);
+ }
+
+ // I.IV) no further special targets exist
+ // Now we have to search for the right target frame by calling findFrame() - but should provide our code
+ // against creation of a new task if no frame could be found.
+ // I said it before - it's allowed for dispatch() only.
+
+ else
+ {
+ sal_Int32 nRightFlags = nSearchFlags & ~css::frame::FrameSearchFlag::CREATE;
+
+ // try to find any existing target and ask him for his dispatcher
+ css::uno::Reference< css::frame::XFrame > xFoundFrame = xDesktop->findFrame(sTargetFrameName, nRightFlags);
+ if (xFoundFrame.is())
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xProvider( xFoundFrame, css::uno::UNO_QUERY );
+ xDispatcher = xProvider->queryDispatch(aURL,SPECIALTARGET_SELF,0);
+ }
+ // if it couldn't be found - but creation was allowed
+ // use special dispatcher for creation or forwarding to the browser
+ else if (nSearchFlags & css::frame::FrameSearchFlag::CREATE)
+ xDispatcher = implts_getOrCreateDispatchHelper( E_CREATEDISPATCHER, xDesktop, sTargetFrameName, nSearchFlags );
+ }
+
+ return xDispatcher;
+}
+
+css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_queryFrameDispatch( const css::uno::Reference< css::frame::XFrame >& xFrame ,
+ const css::util::URL& aURL ,
+ const OUString& sTargetFrameName ,
+ sal_Int32 nSearchFlags )
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatcher;
+
+ // 0) Some URLs are dispatched in a generic way (e.g. by the menu) using the default target "".
+ // But they are specified to use her own fix target. Detect such URLs here and use the correct target.
+
+ // I) handle special cases which not right for using findFrame() first
+
+ // I.I) "_blank", "_default"
+ // It's not the right place to create a new task here. Only the desktop can do that.
+ // Normally the functionality for "_blank" is provided by findFrame() - but that would create it directly
+ // here. that's why we must "intercept" here.
+
+ if (
+ (sTargetFrameName==SPECIALTARGET_BLANK ) ||
+ (sTargetFrameName==SPECIALTARGET_DEFAULT)
+ )
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY );
+ if (xParent.is())
+ xDispatcher = xParent->queryDispatch(aURL, sTargetFrameName, 0); // it's a special target - ignore search flags
+ }
+
+ // I.II) "_beamer"
+ // Special sub frame of a top frame only. Search or create it. ... OK it's currently a little bit HACKI.
+ // Only the sfx (means the controller) can create it.
+
+ else if (sTargetFrameName==SPECIALTARGET_BEAMER)
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xBeamer( xFrame->findFrame( SPECIALTARGET_BEAMER, css::frame::FrameSearchFlag::CHILDREN | css::frame::FrameSearchFlag::SELF ), css::uno::UNO_QUERY );
+ if (xBeamer.is())
+ {
+ xDispatcher = xBeamer->queryDispatch(aURL, SPECIALTARGET_SELF, 0);
+ }
+ else
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xController( xFrame->getController(), css::uno::UNO_QUERY );
+ if (xController.is())
+ // force using of special target - but use original search flags
+ // May the caller used the CREATE flag or not!
+ xDispatcher = xController->queryDispatch(aURL, SPECIALTARGET_BEAMER, nSearchFlags);
+ }
+ }
+
+ // I.IV) "_parent"
+ // Our parent frame (if it exist) should handle this URL.
+
+ else if (sTargetFrameName==SPECIALTARGET_PARENT)
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY );
+ if (xParent.is())
+ // SELF => we must address the parent directly... and not his parent or any other parent!
+ xDispatcher = xParent->queryDispatch(aURL, SPECIALTARGET_SELF, 0);
+ }
+
+ // I.V) "_top"
+ // This request must be forwarded to any parent frame, till we reach a top frame.
+ // If no parent exist, we can handle itself.
+
+ else if (sTargetFrameName==SPECIALTARGET_TOP)
+ {
+ if (xFrame->isTop())
+ {
+ // If we are this top frame itself (means our owner frame)
+ // we should call ourself recursiv with a better target "_self".
+ // So we can share the same code! (see reaction for "_self" inside this method too.)
+ xDispatcher = queryDispatch(aURL,SPECIALTARGET_SELF,0);
+ }
+ else
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY );
+ // Normally if isTop() returned sal_False ... the parent frame MUST(!) exist ...
+ // But it seems to be better to check that here to prevent us against an access violation.
+ if (xParent.is())
+ xDispatcher = xParent->queryDispatch(aURL, SPECIALTARGET_TOP, 0);
+ }
+ }
+
+ // I.VI) "_self", ""
+ // Our owner frame should handle this URL. But we can't do it for all of them.
+ // So we ask the internal set controller first. If he disagree we try to find a registered
+ // protocol handler. If this failed too - we check for a loadable content and in case of true
+ // we load it into the frame by returning specialized dispatch object.
+
+ else if (
+ (sTargetFrameName==SPECIALTARGET_SELF) ||
+ (sTargetFrameName.isEmpty())
+ )
+ {
+ // There exist a hard coded interception for special URLs.
+ if ( aURL.Complete == ".uno:CloseDoc" || aURL.Complete == ".uno:CloseWin" )
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY );
+ // In case the frame is not a top one, is not based on system window and has a parent,
+ // the parent frame should be queried for the correct dispatcher.
+ // See i93473
+ if (
+ !WindowHelper::isTopWindow(xFrame->getContainerWindow()) &&
+ !VCLUnoHelper::GetWindow(xFrame->getContainerWindow())->IsSystemWindow() &&
+ xParent.is()
+ )
+ xDispatcher = xParent->queryDispatch(aURL, SPECIALTARGET_SELF, 0);
+ else
+ xDispatcher = implts_getOrCreateDispatchHelper( E_CLOSEDISPATCHER, xFrame );
+ }
+ else if ( aURL.Complete == ".uno:CloseFrame" )
+ xDispatcher = implts_getOrCreateDispatchHelper( E_CLOSEDISPATCHER, xFrame );
+
+ if ( ! xDispatcher.is())
+ {
+ // Ask our controller for his agreement for these dispatched URL ...
+ // because some URLs are internal and can be handled faster by SFX - which most is the current controller!
+ // But in case of e.g. the bibliography not all queries will be handled successfully here.
+ css::uno::Reference< css::frame::XDispatchProvider > xController( xFrame->getController(), css::uno::UNO_QUERY );
+ if (xController.is())
+ xDispatcher = xController->queryDispatch(aURL, SPECIALTARGET_SELF, 0);
+ }
+
+ // If controller has no fun to dispatch these URL - we must search another right dispatcher.
+ // Search for any registered protocol handler first.
+ if (!xDispatcher.is())
+ xDispatcher = implts_searchProtocolHandler(aURL);
+
+ // Not for controller - not for protocol handler
+ // It should be a loadable content - may be a file. Check it ...
+ // This check is necessary to found out, that
+ // support for some protocols isn't installed by user. May be
+ // "ftp" isn't available. So we suppress creation of our self dispatcher.
+ // The result will be clear. He can't handle it - but he would try it.
+ if (
+ ( ! xDispatcher.is() ) &&
+ ( implts_isLoadableContent(aURL) )
+ )
+ {
+ xDispatcher = implts_getOrCreateDispatchHelper( E_SELFDISPATCHER, xFrame );
+ }
+ }
+
+ // I.VII) no further special handlings exist
+ // Now we have to search for the right target frame by calling findFrame() - but should provide our code
+ // against creation of a new task if no frame could be found.
+ // I said it before - it's allowed for dispatch() only.
+
+ else
+ {
+ sal_Int32 nRightFlags = nSearchFlags & ~css::frame::FrameSearchFlag::CREATE;
+
+ // try to find any existing target and ask him for his dispatcher
+ css::uno::Reference< css::frame::XFrame > xFoundFrame = xFrame->findFrame(sTargetFrameName, nRightFlags);
+ if (xFoundFrame.is())
+ {
+ // Attention: Found target is our own owner frame!
+ // Don't ask him for his dispatcher. We know it already - it's our self dispatch helper.
+ // Otherwise we can start a never ending recursiv call. Why?
+ // Somewhere called our owner frame - he called some interceptor objects - and may by this dispatch provider
+ // is called. If wa use queryDispatch() on our owner frame again - we start this call stack again ... and again.
+ if (xFoundFrame==xFrame)
+ xDispatcher = implts_getOrCreateDispatchHelper( E_SELFDISPATCHER, xFrame );
+ else
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xProvider( xFoundFrame, css::uno::UNO_QUERY );
+ xDispatcher = xProvider->queryDispatch(aURL,SPECIALTARGET_SELF,0);
+ }
+ }
+ else
+ // if it couldn't be found - but creation was allowed
+ // forward request to the desktop.
+ // Note: The given target name must be used to set the name on new created task!
+ // Don't forward request by changing it to a special one e.g _blank.
+ // Use the CREATE flag only to prevent call against further searches.
+ // We already know it - the target must be created new.
+ if (nSearchFlags & css::frame::FrameSearchFlag::CREATE)
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xParent( xFrame->getCreator(), css::uno::UNO_QUERY );
+ if (xParent.is())
+ xDispatcher = xParent->queryDispatch(aURL, sTargetFrameName, css::frame::FrameSearchFlag::CREATE);
+ }
+ }
+
+ return xDispatcher;
+}
+
+/**
+ @short search for a registered protocol handler and ask him for a dispatch object
+ @descr We search a suitable handler inside our cfg package org.openoffice.Office.ProtocolHandler.
+ If we found anyone, we create and initialize it. Initialize means: we set our owner frame on it
+ as context information. He can use it or leave it. Of course - we are aware of handler implementations,
+ which doesn't support initialization. It's an optional feature.
+
+ @param aURL
+ the dispatch URL for which may a handler is registered
+
+ @return A dispatch object if a handler was found and agree with the given URL or <NULL/> otherwise.
+
+ @threadsafe yes
+*/
+css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_searchProtocolHandler( const css::util::URL& aURL )
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatcher;
+ ProtocolHandler aHandler;
+
+ // This member is threadsafe by himself and lives if we live - we don't need any mutex here.
+ if (m_aProtocolHandlerCache.search(aURL,&aHandler))
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xHandler;
+ {
+ SolarMutexGuard g;
+
+ // create it
+ bool bInitialize = true;
+ try
+ {
+ // Only create the protocol handler instance once, the creation is expensive.
+ auto it = m_aProtocolHandlers.find(aHandler.m_sUNOName);
+ if (it == m_aProtocolHandlers.end())
+ {
+ xHandler.set(
+ css::uno::Reference<css::lang::XMultiServiceFactory>(m_xContext->getServiceManager(), css::uno::UNO_QUERY_THROW)
+ ->createInstance(aHandler.m_sUNOName),
+ css::uno::UNO_QUERY);
+
+ // Check if the handler explicitly requested to avoid caching.
+ css::uno::Reference<css::util::XCacheInfo> xCacheInfo(xHandler, css::uno::UNO_QUERY);
+ if (!xCacheInfo.is() || xCacheInfo->isCachingAllowed())
+ {
+ m_aProtocolHandlers.emplace(aHandler.m_sUNOName, xHandler);
+ }
+ }
+ else
+ {
+ xHandler = it->second;
+ bInitialize = false;
+ }
+ }
+ catch(const css::uno::Exception&) {}
+
+ // look if initialization is necessary
+ css::uno::Reference< css::lang::XInitialization > xInit( xHandler, css::uno::UNO_QUERY );
+ if (xInit.is() && bInitialize)
+ {
+ css::uno::Reference< css::frame::XFrame > xOwner( m_xFrame.get(), css::uno::UNO_QUERY );
+ 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.");
+ if (xOwner.is())
+ {
+ try
+ {
+ // but do it only, if all context information is OK
+ css::uno::Sequence< css::uno::Any > lContext{ css::uno::Any(xOwner) };
+ xInit->initialize(lContext);
+ }
+ catch(const css::uno::Exception&) {}
+ }
+ }
+ }
+
+ // ask for his (sub)dispatcher for the given URL
+ if (xHandler.is())
+ xDispatcher = xHandler->queryDispatch(aURL,SPECIALTARGET_SELF,0);
+ }
+
+ return xDispatcher;
+}
+
+/**
+ @short get or create new dispatch helper
+ @descr Sometimes we need some helper implementations to support dispatching of special URLs or commands.
+ But it's not a good idea to hold these services for the whole life time of this provider instance.
+ We should create it on demand...
+ That's why we implement this method. It return an already existing helper or create a new one otherwise.
+
+ @attention The parameter sTarget and nSearchFlags are defaulted to "" and 0!
+ Mostly it depends from the parameter eHelper is they are required or not.
+
+ @param eHelper
+ specify the requested dispatch helper
+ @param xOwner
+ the target of possible dispatch() call on created dispatch helper
+ @param sTarget
+ the target parameter of the original queryDispatch() request
+ @param nSearchFlags
+ the flags parameter of the original queryDispatch() request
+ @return A reference to a dispatch helper.
+
+ @threadsafe yes
+*/
+css::uno::Reference< css::frame::XDispatch > DispatchProvider::implts_getOrCreateDispatchHelper( EDispatchHelper eHelper ,
+ const css::uno::Reference< css::frame::XFrame >& xOwner ,
+ const OUString& sTarget ,
+ sal_Int32 nSearchFlags)
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatchHelper;
+
+ switch (eHelper)
+ {
+ case E_CREATEDISPATCHER :
+ xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, sTarget, nSearchFlags);
+ break;
+
+ case E_BLANKDISPATCHER :
+ {
+ if (xOwner.is())
+ xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, SPECIALTARGET_BLANK, 0);
+ }
+ break;
+
+ case E_DEFAULTDISPATCHER :
+ {
+ if (xOwner.is())
+ xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, SPECIALTARGET_DEFAULT, 0);
+ }
+ break;
+
+ case E_SELFDISPATCHER :
+ xDispatchHelper = new LoadDispatcher(m_xContext, xOwner, SPECIALTARGET_SELF, 0);
+ break;
+
+ case E_CLOSEDISPATCHER :
+ xDispatchHelper = new CloseDispatcher( m_xContext, xOwner, sTarget );
+ break;
+
+ case E_STARTMODULEDISPATCHER :
+ xDispatchHelper = new StartModuleDispatcher( m_xContext );
+ break;
+ }
+
+ return xDispatchHelper;
+}
+
+/**
+ @short check URL for support by our used loader or handler
+ @descr If we must return our own dispatch helper implementations (self, blank, create dispatcher!)
+ we should be sure, that URL describe any loadable content. Otherwise slot/uno URLs
+ will be detected... but there exist nothing for real loading into a target frame!
+
+ @param aURL
+ URL which should be "detected"
+ @return <TRUE/> if somewhere could handle that - <FALSE/> otherwise.
+
+ @threadsafe yes
+*/
+bool DispatchProvider::implts_isLoadableContent( const css::util::URL& aURL )
+{
+ LoadEnv::EContentType eType = LoadEnv::classifyContent(aURL.Complete, css::uno::Sequence< css::beans::PropertyValue >());
+ return ( eType == LoadEnv::E_CAN_BE_LOADED );
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/interceptionhelper.cxx b/framework/source/dispatch/interceptionhelper.cxx
new file mode 100644
index 000000000..57ab28ee4
--- /dev/null
+++ b/framework/source/dispatch/interceptionhelper.cxx
@@ -0,0 +1,270 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/interceptionhelper.hxx>
+
+#include <com/sun/star/frame/XInterceptorInfo.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <osl/diagnose.h>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace com::sun::star;
+
+namespace framework{
+
+InterceptionHelper::InterceptionHelper(const css::uno::Reference< css::frame::XFrame >& xOwner,
+ css::uno::Reference< css::frame::XDispatchProvider > xSlave)
+ : m_xOwnerWeak (xOwner )
+ , m_xSlave (std::move(xSlave ))
+{
+}
+
+InterceptionHelper::~InterceptionHelper()
+{
+}
+
+css::uno::Reference< css::frame::XDispatch > SAL_CALL InterceptionHelper::queryDispatch(const css::util::URL& aURL ,
+ const OUString& sTargetFrameName,
+ sal_Int32 nSearchFlags )
+{
+ css::uno::Reference<css::frame::XDispatchProvider> xInterceptor;
+ // SAFE {
+ {
+ SolarMutexGuard aReadLock;
+
+ // a) first search an interceptor, which match to this URL by its URL pattern registration
+ // Note: if it return NULL - it does not mean an empty interceptor list automatically!
+ InterceptorList::const_iterator pIt = m_lInterceptionRegs.findByPattern(aURL.Complete);
+ if (pIt != m_lInterceptionRegs.end())
+ xInterceptor = pIt->xInterceptor;
+
+ // b) No match by registration - but a valid interceptor list.
+ // Find first interceptor w/o pattern, so we need to query it
+ if (!xInterceptor.is())
+ {
+ for (auto const& lInterceptionReg : m_lInterceptionRegs)
+ {
+ if (!lInterceptionReg.lURLPattern.hasElements())
+ {
+ // no pattern -> need to ask this guy!
+ xInterceptor = lInterceptionReg.xInterceptor;
+ break;
+ }
+ }
+ // if we didn't find any non-pattern interceptor, there's no-one
+ // registered for this command url (we already searched for matching
+ // patterns above)
+ }
+ // c) No registered interceptor => use our direct slave.
+ // This helper exist by design and must be valid everytimes ...
+ // But to be more feature proof - we should check that .-)
+ if (!xInterceptor.is() && m_xSlave.is())
+ xInterceptor = m_xSlave;
+ }
+ // } SAFE
+
+ css::uno::Reference< css::frame::XDispatch > xReturn;
+ if (xInterceptor.is())
+ xReturn = xInterceptor->queryDispatch(aURL, sTargetFrameName, nSearchFlags);
+ return xReturn;
+}
+
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL InterceptionHelper::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor )
+{
+ sal_Int32 c = lDescriptor.getLength();
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatches (c);
+ css::uno::Reference< css::frame::XDispatch >* pDispatches = lDispatches.getArray();
+ const css::frame::DispatchDescriptor* pDescriptor = lDescriptor.getConstArray();
+
+ for (sal_Int32 i=0; i<c; ++i)
+ pDispatches[i] = queryDispatch(pDescriptor[i].FeatureURL, pDescriptor[i].FrameName, pDescriptor[i].SearchFlags);
+
+ return lDispatches;
+}
+
+void SAL_CALL InterceptionHelper::registerDispatchProviderInterceptor(const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor)
+{
+ // reject incorrect calls of this interface method
+ css::uno::Reference< css::frame::XDispatchProvider > xThis(this);
+ if (!xInterceptor.is())
+ throw css::uno::RuntimeException("NULL references not allowed as in parameter", xThis);
+
+ // Fill a new info structure for new interceptor.
+ // Save his reference and try to get an additional URL/pattern list from him.
+ // If no list exist register these interceptor for all dispatch events with "*"!
+ InterceptorInfo aInfo;
+
+ aInfo.xInterceptor = xInterceptor;
+ css::uno::Reference< css::frame::XInterceptorInfo > xInfo(xInterceptor, css::uno::UNO_QUERY);
+ if (xInfo.is())
+ aInfo.lURLPattern = xInfo->getInterceptedURLs();
+ else
+ aInfo.lURLPattern = { "*" };
+
+ // SAFE {
+ SolarMutexClearableGuard aWriteLock;
+
+ // a) no interceptor at all - set this instance as master for given interceptor
+ // and set our slave as its slave - and put this interceptor to the list.
+ // Its place there doesn't matter. Because this list is currently empty.
+ if (m_lInterceptionRegs.empty())
+ {
+ xInterceptor->setMasterDispatchProvider(xThis );
+ xInterceptor->setSlaveDispatchProvider (m_xSlave);
+ m_lInterceptionRegs.push_back(aInfo);
+ }
+
+ // b) OK - there is at least one interceptor already registered.
+ // It's slave and it's master must be valid references ...
+ // because we created it.
+
+ // insert it before any other existing interceptor - means at the beginning of our list.
+ else
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xSlaveD = m_lInterceptionRegs.begin()->xInterceptor;
+ css::uno::Reference< css::frame::XDispatchProviderInterceptor > xSlaveI (xSlaveD , css::uno::UNO_QUERY);
+
+ xInterceptor->setMasterDispatchProvider(xThis );
+ xInterceptor->setSlaveDispatchProvider (xSlaveD );
+ xSlaveI->setMasterDispatchProvider (aInfo.xInterceptor);
+
+ m_lInterceptionRegs.push_front(aInfo);
+ }
+
+ css::uno::Reference< css::frame::XFrame > xOwner(m_xOwnerWeak.get(), css::uno::UNO_QUERY);
+
+ aWriteLock.clear();
+ // } SAFE
+
+ // Don't forget to send a frame action event "context changed".
+ // Any cached dispatch objects must be validated now!
+ if (xOwner.is())
+ xOwner->contextChanged();
+}
+
+void SAL_CALL InterceptionHelper::releaseDispatchProviderInterceptor(const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor)
+{
+ // reject wrong calling of this interface method
+ css::uno::Reference< css::frame::XDispatchProvider > xThis(this);
+ if (!xInterceptor.is())
+ throw css::uno::RuntimeException("NULL references not allowed as in parameter", xThis);
+
+ // SAFE {
+ SolarMutexClearableGuard aWriteLock;
+
+ // search this interceptor ...
+ // If it could be located inside cache -
+ // use its slave/master relations to update the interception list;
+ // set empty references for it as new master and slave;
+ // and release it from out cache.
+ InterceptorList::iterator pIt = m_lInterceptionRegs.findByReference(xInterceptor);
+ if (pIt != m_lInterceptionRegs.end())
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xSlaveD = xInterceptor->getSlaveDispatchProvider();
+ css::uno::Reference< css::frame::XDispatchProvider > xMasterD = xInterceptor->getMasterDispatchProvider();
+ css::uno::Reference< css::frame::XDispatchProviderInterceptor > xSlaveI (xSlaveD , css::uno::UNO_QUERY);
+ css::uno::Reference< css::frame::XDispatchProviderInterceptor > xMasterI (xMasterD , css::uno::UNO_QUERY);
+
+ if (xMasterI.is())
+ xMasterI->setSlaveDispatchProvider(xSlaveD);
+
+ if (xSlaveI.is())
+ {
+ try
+ {
+ xSlaveI->setMasterDispatchProvider(xMasterD);
+ }
+ catch (const lang::DisposedException&)
+ {
+ TOOLS_WARN_EXCEPTION("fwk.dispatch",
+ "InterceptionHelper::releaseDispatchProviderInterceptor: "
+ "xSlaveI is disposed: ");
+ }
+ }
+
+ xInterceptor->setSlaveDispatchProvider (css::uno::Reference< css::frame::XDispatchProvider >());
+ xInterceptor->setMasterDispatchProvider(css::uno::Reference< css::frame::XDispatchProvider >());
+
+ m_lInterceptionRegs.erase(pIt);
+ }
+
+ css::uno::Reference< css::frame::XFrame > xOwner(m_xOwnerWeak.get(), css::uno::UNO_QUERY);
+
+ aWriteLock.clear();
+ // } SAFE
+
+ // Don't forget to send a frame action event "context changed".
+ // Any cached dispatch objects must be validated now!
+ if (xOwner.is())
+ xOwner->contextChanged();
+}
+
+#define FORCE_DESTRUCTION_OF_INTERCEPTION_CHAIN
+void SAL_CALL InterceptionHelper::disposing(const css::lang::EventObject& aEvent)
+{
+ #ifdef FORCE_DESTRUCTION_OF_INTERCEPTION_CHAIN
+ // SAFE ->
+ SolarMutexResettableGuard aReadLock;
+
+ // check call... we accept such disposing calls only from our owner frame.
+ css::uno::Reference< css::frame::XFrame > xOwner(m_xOwnerWeak.get(), css::uno::UNO_QUERY);
+ if (aEvent.Source != xOwner)
+ return;
+
+ // Because every interceptor hold at least one reference to us ... and we destruct this list
+ // of interception objects ... we should hold ourself alive .-)
+ css::uno::Reference< css::frame::XDispatchProvider > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY_THROW);
+
+ // We need a full copy of all currently registered interceptor objects.
+ // Otherwise we can't iterate over this vector without the risk, that our iterator will be invalid.
+ // Because this vetor will be influenced by every deregistered interceptor.
+ InterceptionHelper::InterceptorList aCopy = m_lInterceptionRegs;
+
+ aReadLock.clear();
+ // <- SAFE
+
+ for (auto & elem : aCopy)
+ {
+ if (elem.xInterceptor.is())
+ {
+ css::uno::Reference< css::frame::XDispatchProviderInterceptor > xInterceptor(elem.xInterceptor, css::uno::UNO_QUERY_THROW);
+ releaseDispatchProviderInterceptor(xInterceptor);
+ elem.xInterceptor.clear();
+ }
+ }
+
+ aCopy.clear();
+
+ #if OSL_DEBUG_LEVEL > 0
+ // SAFE ->
+ aReadLock.reset();
+ if (!m_lInterceptionRegs.empty() )
+ OSL_FAIL("There are some pending interceptor objects, which seems to be registered during (!) the destruction of a frame.");
+ aReadLock.clear();
+ // <- SAFE
+ #endif // ODL_DEBUG_LEVEL>0
+
+ #endif // FORCE_DESTRUCTION_OF_INTERCEPTION_CHAIN
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/isstartmoduledispatch.hxx b/framework/source/dispatch/isstartmoduledispatch.hxx
new file mode 100644
index 000000000..a968a36f6
--- /dev/null
+++ b/framework/source/dispatch/isstartmoduledispatch.hxx
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/util/URL.hpp>
+
+namespace framework
+{
+inline bool isStartModuleDispatch(css::util::URL const& url)
+{
+ return url.Complete == ".uno:ShowStartModule";
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/loaddispatcher.cxx b/framework/source/dispatch/loaddispatcher.cxx
new file mode 100644
index 000000000..04ea5cf5c
--- /dev/null
+++ b/framework/source/dispatch/loaddispatcher.cxx
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/loaddispatcher.hxx>
+#include <loadenv/loadenvexception.hxx>
+#include <sal/log.hxx>
+
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <utility>
+
+namespace framework{
+
+LoadDispatcher::LoadDispatcher(const css::uno::Reference< css::uno::XComponentContext >& xContext ,
+ const css::uno::Reference< css::frame::XFrame >& xOwnerFrame ,
+ OUString sTargetName ,
+ sal_Int32 nSearchFlags)
+ : m_xOwnerFrame (xOwnerFrame )
+ , m_sTarget (std::move(sTargetName ))
+ , m_nSearchFlags(nSearchFlags)
+ , m_aLoader (xContext )
+{
+}
+
+LoadDispatcher::~LoadDispatcher()
+{
+}
+
+void SAL_CALL LoadDispatcher::dispatchWithNotification(const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ impl_dispatch( aURL, lArguments, xListener );
+}
+
+void SAL_CALL LoadDispatcher::dispatch(const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments)
+{
+ impl_dispatch( aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >() );
+}
+
+css::uno::Any SAL_CALL LoadDispatcher::dispatchWithReturnValue( const css::util::URL& rURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments )
+{
+ return impl_dispatch( rURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >());
+}
+
+void SAL_CALL LoadDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/,
+ const css::util::URL& /*aURL*/ )
+{
+}
+
+void SAL_CALL LoadDispatcher::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/,
+ const css::util::URL& /*aURL*/ )
+{
+}
+
+css::uno::Any LoadDispatcher::impl_dispatch( const css::util::URL& rURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ // Attention: May be nobody outside hold such temp. dispatch object alive (because
+ // the container in which we resist isn't implemented threadsafe but updated by a timer
+ // and clear our reference...) we should hold us self alive!
+ css::uno::Reference< css::uno::XInterface > xThis(static_cast< css::frame::XNotifyingDispatch* >(this), css::uno::UNO_QUERY);
+
+ osl::MutexGuard g(m_mutex);
+
+ // We are the only client of this load env object... but
+ // may a dispatch request before is still in progress (?!).
+ // Then we should wait a little bit and block this new request.
+ // In case we run into the timeout, we should reject this new request
+ // and return "FAILED" as result. Otherwise we can start this new operation.
+ if (!m_aLoader.waitWhileLoading(2000)) // => 2 sec.
+ {
+ if (xListener.is())
+ xListener->dispatchFinished(
+ css::frame::DispatchResultEvent(xThis, css::frame::DispatchResultState::DONTKNOW, css::uno::Any())); // DONTKNOW? ... not really started ... not really failed :-)
+ }
+
+ css::uno::Reference< css::frame::XFrame > xBaseFrame(m_xOwnerFrame.get(), css::uno::UNO_QUERY);
+ if (!xBaseFrame.is() && xListener.is())
+ xListener->dispatchFinished(
+ css::frame::DispatchResultEvent(xThis, css::frame::DispatchResultState::FAILURE, css::uno::Any()));
+
+ // OK ... now the internal loader seems to be usable for new requests
+ // and our owner frame seems to be valid for such operations.
+ // Initialize it with all new but needed properties and start the loading.
+ css::uno::Reference< css::lang::XComponent > xComponent;
+ try
+ {
+ m_aLoader.startLoading( rURL.Complete, lArguments, xBaseFrame, m_sTarget, m_nSearchFlags, LoadEnvFeatures::AllowContentHandler | LoadEnvFeatures::WorkWithUI);
+ m_aLoader.waitWhileLoading(); // wait for ever!
+ xComponent = m_aLoader.getTargetComponent();
+
+ // TODO thinking about asynchronous operations and listener support
+ }
+ catch(const LoadEnvException& e)
+ {
+ SAL_WARN(
+ "fwk.dispatch",
+ "caught LoadEnvException " << +e.m_nID << " \"" << e.m_sMessage
+ << "\""
+ << (e.m_exOriginal.has<css::uno::Exception>()
+ ? (", " + e.m_exOriginal.getValueTypeName() + " \""
+ + e.m_exOriginal.get<css::uno::Exception>().Message
+ + "\"")
+ : OUString())
+ << " while dispatching <" << rURL.Complete << ">");
+ xComponent.clear();
+ }
+
+ if (xListener.is())
+ {
+ if (xComponent.is())
+ xListener->dispatchFinished(
+ css::frame::DispatchResultEvent(xThis, css::frame::DispatchResultState::SUCCESS, css::uno::Any()));
+ else
+ xListener->dispatchFinished(
+ css::frame::DispatchResultEvent(xThis, css::frame::DispatchResultState::FAILURE, css::uno::Any()));
+ }
+
+ // return the model - like loadComponentFromURL()
+ css::uno::Any aRet;
+ if ( xComponent.is () )
+ aRet <<= xComponent;
+
+ return aRet;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/mailtodispatcher.cxx b/framework/source/dispatch/mailtodispatcher.cxx
new file mode 100644
index 000000000..9149b9b40
--- /dev/null
+++ b/framework/source/dispatch/mailtodispatcher.cxx
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/mailtodispatcher.hxx>
+#include <services.h>
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/system/SystemShellExecute.hpp>
+#include <com/sun/star/system/SystemShellExecuteException.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <utility>
+
+namespace framework{
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL MailToDispatcher::getImplementationName()
+{
+ return "com.sun.star.comp.framework.MailToDispatcher";
+}
+
+sal_Bool SAL_CALL MailToDispatcher::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL MailToDispatcher::getSupportedServiceNames()
+{
+ return { SERVICENAME_PROTOCOLHANDLER };
+}
+
+
+/**
+ @short standard ctor
+ @descr This initializes a new instance of this class with needed information for work.
+
+ @param rxContext
+ reference to uno servicemanager for creation of new services
+*/
+MailToDispatcher::MailToDispatcher( css::uno::Reference< css::uno::XComponentContext > xContext )
+ : m_xContext (std::move( xContext ))
+{
+}
+
+/**
+ @short standard dtor
+*/
+MailToDispatcher::~MailToDispatcher()
+{
+}
+
+/**
+ @short decide if this dispatch implementation can be used for requested URL or not
+ @descr A protocol handler is registered for a URL pattern inside configuration and will
+ be asked by the generic dispatch mechanism inside framework, if he can handle this
+ special URL which match his registration. He can agree by returning of a valid dispatch
+ instance or disagree by returning <NULL/>.
+ We don't create new dispatch instances here really - we return THIS as result to handle it
+ at the same implementation.
+*/
+css::uno::Reference< css::frame::XDispatch > SAL_CALL MailToDispatcher::queryDispatch( const css::util::URL& aURL ,
+ const OUString& /*sTarget*/ ,
+ sal_Int32 /*nFlags*/ )
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatcher;
+ if (aURL.Complete.startsWith("mailto:"))
+ xDispatcher = this;
+ return xDispatcher;
+}
+
+/**
+ @short do the same like dispatch() but for multiple requests at the same time
+*/
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL MailToDispatcher::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor )
+{
+ sal_Int32 nCount = lDescriptor.getLength();
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount );
+ auto lDispatcherRange = asNonConstRange(lDispatcher);
+ for( sal_Int32 i=0; i<nCount; ++i )
+ {
+ lDispatcherRange[i] = queryDispatch(
+ lDescriptor[i].FeatureURL,
+ lDescriptor[i].FrameName,
+ lDescriptor[i].SearchFlags);
+ }
+ return lDispatcher;
+}
+
+/**
+ @short dispatch URL with arguments
+ @descr We use threadsafe internal method to do so. It returns a state value - but we ignore it.
+ Because we don't support status listener notifications here. Status events are not guaranteed -
+ and we call another service internally which doesn't return any notifications too.
+
+ @param aURL
+ mail URL which should be executed
+ @param lArguments
+ list of optional arguments for this mail request
+*/
+void SAL_CALL MailToDispatcher::dispatch( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/ )
+{
+ // dispatch() is an [oneway] call ... and may our user release his reference to us immediately.
+ // So we should hold us self alive till this call ends.
+ css::uno::Reference< css::frame::XNotifyingDispatch > xSelfHold(this);
+ implts_dispatch(aURL);
+ // No notification for status listener!
+}
+
+/**
+ @short dispatch with guaranteed notifications about success
+ @descr We use threadsafe internal method to do so. Return state of this function will be used
+ for notification if an optional listener is given.
+
+ @param aURL
+ mail URL which should be executed
+ @param lArguments
+ list of optional arguments for this mail request
+ @param xListener
+ reference to a valid listener for state events
+*/
+void SAL_CALL MailToDispatcher::dispatchWithNotification( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ // This class was designed to die by reference. And if user release his reference to us immediately after calling this method
+ // we can run into some problems. So we hold us self alive till this method ends.
+ // Another reason: We can use this reference as source of sending event at the end too.
+ css::uno::Reference< css::frame::XNotifyingDispatch > xThis(this);
+
+ bool bState = implts_dispatch(aURL);
+ if (xListener.is())
+ {
+ css::frame::DispatchResultEvent aEvent;
+ if (bState)
+ aEvent.State = css::frame::DispatchResultState::SUCCESS;
+ else
+ aEvent.State = css::frame::DispatchResultState::FAILURE;
+ aEvent.Source = xThis;
+
+ xListener->dispatchFinished( aEvent );
+ }
+}
+
+/**
+ @short threadsafe helper for dispatch calls
+ @descr We support two interfaces for the same process - dispatch URLs. That the reason for this internal
+ function. It implements the real dispatch operation and returns a state value which inform caller
+ about success. He can notify listener then by using this return value.
+
+ @param aURL
+ mail URL which should be executed
+
+ @return <TRUE/> if dispatch could be started successfully
+ Note: Our internal used shell executor doesn't return any state value - so we must
+ believe that call was successful.
+ <FALSE/> if necessary resource couldn't be created or an exception was thrown.
+*/
+bool MailToDispatcher::implts_dispatch( const css::util::URL& aURL )
+{
+ bool bSuccess = false;
+
+ css::uno::Reference< css::system::XSystemShellExecute > xSystemShellExecute = css::system::SystemShellExecute::create( m_xContext );
+
+ try
+ {
+ // start mail client
+ // Because there is no notification about success - we use case of
+ // no detected exception as SUCCESS - FAILED otherwise.
+ xSystemShellExecute->execute( aURL.Complete, OUString(), css::system::SystemShellExecuteFlags::URIS_ONLY );
+ bSuccess = true;
+ }
+ catch (const css::lang::IllegalArgumentException&)
+ {
+ }
+ catch (const css::system::SystemShellExecuteException&)
+ {
+ }
+
+ return bSuccess;
+}
+
+/**
+ @short add/remove listener for state events
+ @descr Because we use an external process to forward such mail URLs, and this process doesn't
+ return any notifications about success or failed state - we don't support such status
+ listener. We have no status to send.
+
+ @param xListener
+ reference to a valid listener for state events
+ @param aURL
+ URL about listener will be informed, if something occurred
+*/
+void SAL_CALL MailToDispatcher::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ ,
+ const css::util::URL& /*aURL*/ )
+{
+ // not supported yet
+}
+
+void SAL_CALL MailToDispatcher::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ ,
+ const css::util::URL& /*aURL*/ )
+{
+ // not supported yet
+}
+
+} // namespace framework
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_MailToDispatcher_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::MailToDispatcher(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/oxt_handler.cxx b/framework/source/dispatch/oxt_handler.cxx
new file mode 100644
index 000000000..df956c7ee
--- /dev/null
+++ b/framework/source/dispatch/oxt_handler.cxx
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/oxt_handler.hxx>
+#include <unotools/mediadescriptor.hxx>
+
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/task/XJobExecutor.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <tools/long.hxx>
+#include <utility>
+
+namespace framework{
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL Oxt_Handler::getImplementationName()
+{
+ return "com.sun.star.comp.framework.OXTFileHandler";
+}
+
+sal_Bool SAL_CALL Oxt_Handler::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL Oxt_Handler::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ContentHandler" };
+}
+
+
+/*-************************************************************************************************************
+ @short standard ctor
+ @descr These initialize a new instance of this class with needed information for work.
+
+ @seealso using at owner
+
+ @param "xFactory", reference to service manager for creation of new services
+ @onerror Show an assertion and do nothing else.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+Oxt_Handler::Oxt_Handler( css::uno::Reference< css::uno::XComponentContext > xContext )
+ : m_xContext (std::move( xContext ))
+{
+}
+
+/*-************************************************************************************************************
+ @short standard dtor
+*//*-*************************************************************************************************************/
+Oxt_Handler::~Oxt_Handler()
+{
+}
+
+/*-************************************************************************************************************
+ @interface css::frame::XDispatch
+
+ @short try to load audio file
+ @descr This method try to load given audio file by URL and play it. We use vcl/Sound class to do that.
+ Playing of sound is asynchron every time.
+
+ @attention We must hold us alive by ourself ... because we use async. vcl sound player ... but playing is started
+ in async interface call "dispatch()" too. And caller forget us immediately. But then our uno ref count
+ will decreased to 0 and will die. The only solution is to use own reference to our implementation.
+ But we do it for really started jobs only and release it during call back of vcl.
+
+ @seealso class vcl/Sound
+ @seealso method implts_PlayerNotify()
+
+ @param "aURL" , URL to dispatch.
+ @param "lArguments", list of optional arguments.
+ @onerror We do nothing.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+void SAL_CALL Oxt_Handler::dispatchWithNotification( const css::util::URL& aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ std::unique_lock g(m_mutex);
+
+ css::uno::Sequence< css::uno::Any > lParams{ css::uno::Any(aURL.Main) };
+
+ css::uno::Reference< css::uno::XInterface > xService = m_xContext->getServiceManager()->createInstanceWithArgumentsAndContext( "com.sun.star.deployment.ui.PackageManagerDialog", lParams, m_xContext );
+ css::uno::Reference< css::task::XJobExecutor > xExecutable( xService, css::uno::UNO_QUERY );
+ if ( xExecutable.is() )
+ xExecutable->trigger( OUString() );
+
+ if ( xListener.is() )
+ {
+ css::frame::DispatchResultEvent aEvent;
+ aEvent.State = css::frame::DispatchResultState::SUCCESS;
+ xListener->dispatchFinished( aEvent );
+ }
+}
+
+void SAL_CALL Oxt_Handler::dispatch( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments )
+{
+ dispatchWithNotification( aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >() );
+}
+
+/*-************************************************************************************************************
+ @interface css::document::XExtendedFilterDetection
+
+ @short try to detect file (given as argument included in "lDescriptor")
+ @descr We try to detect, if given file could be handled by this class and is a well known one.
+ If it is - we return right internal type name - otherwise we return nothing!
+ So call can search for another detect service and ask him too.
+
+ @attention a) We don't need any mutex here ... because we don't use any member!
+ b) Don't use internal player instance "m_pPlayer" to detect given sound file!
+ It's not necessary to do that ... and we can use temp. variable to do the same.
+ This way is easy - we don't must synchronize it with currently played sounds!
+ Another reason to do so ... We are a listener on our internal ma_Player object.
+ If you would call "IsSoundFile()" on this instance, he would call us back and
+ we make some unnecessary things ...
+ @param "lDescriptor", description of file to detect
+ @return Internal type name which match this file ... or nothing if it is unknown.
+
+ @onerror We return nothing.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+OUString SAL_CALL Oxt_Handler::detect( css::uno::Sequence< css::beans::PropertyValue >& lDescriptor )
+{
+ // Our default is "nothing". So we can return it, if detection failed or file type is really unknown.
+ OUString sTypeName;
+
+ // Analyze given descriptor to find filename or input stream or...
+ utl::MediaDescriptor aDescriptor( lDescriptor );
+ OUString sURL = aDescriptor.getUnpackedValueOrDefault( utl::MediaDescriptor::PROP_URL, OUString() );
+
+ tools::Long nLength = sURL.getLength();
+ if ( ( nLength > 4 ) && sURL.matchIgnoreAsciiCase( ".oxt", nLength-4 ) )
+ {
+ // "IsSoundFile" differs between different "wav" and "au" file versions...
+ // couldn't return this information... because: it use the OS to detect it!
+ // I think we can than following ones:
+ // a) look for given extension of url to map our type decision HARD CODED!!!
+ // b) return preferred type every time... it's easy :-)
+ sTypeName = "oxt_OpenOffice_Extension";
+ aDescriptor[utl::MediaDescriptor::PROP_TYPENAME] <<= sTypeName;
+ aDescriptor >> lDescriptor;
+ }
+
+ // Return our decision.
+ return sTypeName;
+}
+
+} // namespace framework
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_Oxt_Handler_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::Oxt_Handler(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/popupmenudispatcher.cxx b/framework/source/dispatch/popupmenudispatcher.cxx
new file mode 100644
index 000000000..3220ceece
--- /dev/null
+++ b/framework/source/dispatch/popupmenudispatcher.cxx
@@ -0,0 +1,269 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/popupmenudispatcher.hxx>
+#include <services.h>
+#include <properties.h>
+
+#include <com/sun/star/frame/XLayoutManager2.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <com/sun/star/ui/XUIElement.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+namespace framework{
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::cppu;
+using namespace ::osl;
+
+PopupMenuDispatcher::PopupMenuDispatcher(
+ uno::Reference< XComponentContext > xContext )
+ : m_xContext (std::move( xContext ))
+ , m_bAlreadyDisposed ( false )
+ , m_bActivateListener ( false )
+{
+}
+
+PopupMenuDispatcher::~PopupMenuDispatcher()
+{
+ // Warn programmer if he forgot to dispose this instance.
+ // We must release all our references ...
+ // and a dtor isn't the best place to do that!
+}
+
+OUString SAL_CALL PopupMenuDispatcher::getImplementationName()
+{
+ return "com.sun.star.comp.framework.PopupMenuControllerDispatcher";
+}
+
+sal_Bool SAL_CALL PopupMenuDispatcher::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL PopupMenuDispatcher::getSupportedServiceNames()
+{
+ return { SERVICENAME_PROTOCOLHANDLER };
+}
+
+void SAL_CALL PopupMenuDispatcher::initialize( const css::uno::Sequence< css::uno::Any >& lArguments )
+{
+ css::uno::Reference< css::frame::XFrame > xFrame;
+
+ SolarMutexGuard g;
+ for (int a=0; a<lArguments.getLength(); ++a)
+ {
+ if (a==0)
+ {
+ lArguments[a] >>= xFrame;
+ m_xWeakFrame = xFrame;
+
+ m_bActivateListener = true;
+ uno::Reference< css::frame::XFrameActionListener > xFrameActionListener(this);
+ xFrame->addFrameActionListener( xFrameActionListener );
+ }
+ }
+}
+
+css::uno::Reference< css::frame::XDispatch >
+SAL_CALL PopupMenuDispatcher::queryDispatch(
+ const css::util::URL& rURL ,
+ const OUString& sTarget ,
+ sal_Int32 nFlags )
+{
+ if ( !rURL.Complete.startsWith( "vnd.sun.star.popup:" ) )
+ return {};
+
+ // --- SAFE ---
+ SolarMutexClearableGuard aGuard;
+ impl_RetrievePopupControllerQuery();
+ if ( !m_xUriRefFactory.is() )
+ m_xUriRefFactory = css::uri::UriReferenceFactory::create( m_xContext );
+
+ css::uno::Reference< css::container::XNameAccess > xPopupCtrlQuery( m_xPopupCtrlQuery );
+ aGuard.clear();
+ // --- SAFE ---
+
+ if ( !xPopupCtrlQuery.is() )
+ return {};
+
+ css::uno::Reference< css::frame::XDispatch > xDispatch;
+
+ try
+ {
+ // Just use the main part of the URL for popup menu controllers
+ sal_Int32 nSchemePart( 0 );
+ OUString aBaseURL( "vnd.sun.star.popup:" );
+ OUString aURL( rURL.Complete );
+
+ nSchemePart = aURL.indexOf( ':' );
+ if (( nSchemePart > 0 ) &&
+ ( aURL.getLength() > ( nSchemePart+1 )))
+ {
+ sal_Int32 nQueryPart = aURL.indexOf( '?', nSchemePart );
+ if ( nQueryPart > 0 )
+ aBaseURL += aURL.subView( nSchemePart+1, nQueryPart-(nSchemePart+1) );
+ else if ( nQueryPart == -1 )
+ aBaseURL += aURL.subView( nSchemePart+1 );
+ }
+
+ css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider;
+
+ // Find popup menu controller using the base URL
+ xPopupCtrlQuery->getByName( aBaseURL ) >>= xDispatchProvider;
+
+ // Ask popup menu dispatch provider for dispatch object
+ if ( xDispatchProvider.is() )
+ xDispatch = xDispatchProvider->queryDispatch( rURL, sTarget, nFlags );
+ }
+ catch ( const RuntimeException& )
+ {
+ throw;
+ }
+ catch ( const Exception& )
+ {
+ }
+ return xDispatch;
+}
+
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL
+PopupMenuDispatcher::queryDispatches(
+ const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor )
+{
+ sal_Int32 nCount = lDescriptor.getLength();
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount );
+ auto lDispatcherRange = asNonConstRange(lDispatcher);
+ for( sal_Int32 i=0; i<nCount; ++i )
+ {
+ lDispatcherRange[i] = queryDispatch(
+ lDescriptor[i].FeatureURL,
+ lDescriptor[i].FrameName,
+ lDescriptor[i].SearchFlags);
+ }
+ return lDispatcher;
+}
+
+void SAL_CALL PopupMenuDispatcher::dispatch( const URL& /*aURL*/, const Sequence< PropertyValue >& /*seqProperties*/ )
+{
+}
+
+void SAL_CALL PopupMenuDispatcher::addStatusListener( const uno::Reference< XStatusListener >& /*xControl*/,
+ const URL& /*aURL*/ )
+{
+}
+
+void SAL_CALL PopupMenuDispatcher::removeStatusListener( const uno::Reference< XStatusListener >& /*xControl*/,
+ const URL& /*aURL*/ )
+{
+}
+
+void SAL_CALL PopupMenuDispatcher::frameAction( const FrameActionEvent& aEvent )
+{
+ SolarMutexGuard g;
+ if (( aEvent.Action == css::frame::FrameAction_COMPONENT_DETACHING ) ||
+ ( aEvent.Action == css::frame::FrameAction_COMPONENT_ATTACHED ))
+ {
+ // Reset query reference to requery it again next time
+ m_xPopupCtrlQuery.clear();
+ }
+}
+
+void SAL_CALL PopupMenuDispatcher::disposing( const EventObject& )
+{
+ SolarMutexGuard g;
+ // Safe impossible cases
+ SAL_WARN_IF( m_bAlreadyDisposed, "fwk", "MenuDispatcher::disposing(): Object already disposed .. don't call it again!" );
+
+ if( m_bAlreadyDisposed )
+ return;
+
+ m_bAlreadyDisposed = true;
+
+ if ( m_bActivateListener )
+ {
+ uno::Reference< XFrame > xFrame( m_xWeakFrame.get(), UNO_QUERY );
+ if ( xFrame.is() )
+ {
+ xFrame->removeFrameActionListener( uno::Reference< XFrameActionListener >(this) );
+ m_bActivateListener = false;
+ }
+ }
+
+ // Forget our factory.
+ m_xContext.clear();
+}
+
+void PopupMenuDispatcher::impl_RetrievePopupControllerQuery()
+{
+ if ( m_xPopupCtrlQuery.is() )
+ return;
+
+ css::uno::Reference< css::frame::XLayoutManager2 > xLayoutManager;
+ css::uno::Reference< css::frame::XFrame > xFrame( m_xWeakFrame );
+
+ if ( !xFrame.is() )
+ return;
+
+ css::uno::Reference< css::beans::XPropertySet > xPropSet( xFrame, css::uno::UNO_QUERY );
+ if ( !xPropSet.is() )
+ return;
+
+ try
+ {
+ xPropSet->getPropertyValue( FRAME_PROPNAME_ASCII_LAYOUTMANAGER ) >>= xLayoutManager;
+
+ if ( xLayoutManager.is() )
+ {
+ css::uno::Reference< css::ui::XUIElement > xMenuBar = xLayoutManager->getElement( "private:resource/menubar/menubar" );
+
+ m_xPopupCtrlQuery.set( xMenuBar, css::uno::UNO_QUERY );
+ }
+ }
+ catch ( const css::uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( const css::uno::Exception& )
+ {
+ }
+}
+
+} // namespace framework
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_PopupMenuDispatcher_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::PopupMenuDispatcher(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/servicehandler.cxx b/framework/source/dispatch/servicehandler.cxx
new file mode 100644
index 000000000..14dea0603
--- /dev/null
+++ b/framework/source/dispatch/servicehandler.cxx
@@ -0,0 +1,260 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/servicehandler.hxx>
+#include <services.h>
+
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/task/XJobExecutor.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+#include <tools/diagnose_ex.h>
+#include <cppuhelper/supportsservice.hxx>
+#include <utility>
+
+namespace framework{
+
+constexpr OUStringLiteral PROTOCOL_VALUE = u"service:";
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL ServiceHandler::getImplementationName()
+{
+ return "com.sun.star.comp.framework.ServiceHandler";
+}
+
+sal_Bool SAL_CALL ServiceHandler::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL ServiceHandler::getSupportedServiceNames()
+{
+ return { SERVICENAME_PROTOCOLHANDLER };
+}
+
+
+/**
+ @short standard ctor
+ @descr This initializes a new instance of this class with needed information for work.
+
+ @param xFactory
+ reference to uno servicemanager for creation of new services
+*/
+ServiceHandler::ServiceHandler( css::uno::Reference< css::uno::XComponentContext > xContext )
+ : m_xContext (std::move( xContext ))
+{
+}
+
+/**
+ @short standard dtor
+*/
+ServiceHandler::~ServiceHandler()
+{
+}
+
+/**
+ @short decide if this dispatch implementation can be used for requested URL or not
+ @descr A protocol handler is registered for a URL pattern inside configuration and will
+ be asked by the generic dispatch mechanism inside framework, if he can handle this
+ special URL which match his registration. He can agree by returning of a valid dispatch
+ instance or disagree by returning <NULL/>.
+ We don't create new dispatch instances here really - we return THIS as result to handle it
+ at the same implementation.
+*/
+css::uno::Reference< css::frame::XDispatch > SAL_CALL ServiceHandler::queryDispatch( const css::util::URL& aURL ,
+ const OUString& /*sTarget*/ ,
+ sal_Int32 /*nFlags*/ )
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatcher;
+ if (aURL.Complete.startsWith(PROTOCOL_VALUE))
+ xDispatcher = this;
+ return xDispatcher;
+}
+
+/**
+ @short do the same like dispatch() but for multiple requests at the same time
+*/
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL ServiceHandler::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor )
+{
+ sal_Int32 nCount = lDescriptor.getLength();
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount );
+ auto lDispatcherRange = asNonConstRange(lDispatcher);
+ for( sal_Int32 i=0; i<nCount; ++i )
+ {
+ lDispatcherRange[i] = queryDispatch(
+ lDescriptor[i].FeatureURL,
+ lDescriptor[i].FrameName,
+ lDescriptor[i].SearchFlags);
+ }
+ return lDispatcher;
+}
+
+/**
+ @short dispatch URL with arguments
+ @descr We use threadsafe internal method to do so. It returns a state value - but we ignore it.
+ Because we don't support status listener notifications here.
+
+ @param aURL
+ uno URL which should be executed
+ @param lArguments
+ list of optional arguments for this request
+*/
+void SAL_CALL ServiceHandler::dispatch( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/ )
+{
+ // dispatch() is an [oneway] call ... and may our user release his reference to us immediately.
+ // So we should hold us self alive till this call ends.
+ css::uno::Reference< css::frame::XNotifyingDispatch > xSelfHold(this);
+ implts_dispatch(aURL);
+ // No notification for status listener!
+}
+
+/**
+ @short dispatch with guaranteed notifications about success
+ @descr We use threadsafe internal method to do so. Return state of this function will be used
+ for notification if an optional listener is given.
+
+ @param aURL
+ uno URL which should be executed
+ @param lArguments
+ list of optional arguments for this request
+ @param xListener
+ optional listener for state events
+*/
+void SAL_CALL ServiceHandler::dispatchWithNotification( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ // This class was designed to die by reference. And if user release his reference to us immediately after calling this method
+ // we can run into some problems. So we hold us self alive till this method ends.
+ // Another reason: We can use this reference as source of sending event at the end too.
+ css::uno::Reference< css::frame::XNotifyingDispatch > xThis(this);
+
+ css::uno::Reference< css::uno::XInterface > xService = implts_dispatch(aURL);
+ if (xListener.is())
+ {
+ css::frame::DispatchResultEvent aEvent;
+ if (xService.is())
+ aEvent.State = css::frame::DispatchResultState::SUCCESS;
+ else
+ aEvent.State = css::frame::DispatchResultState::FAILURE;
+ aEvent.Result <<= xService; // may NULL for state=FAILED!
+ aEvent.Source = xThis;
+
+ xListener->dispatchFinished( aEvent );
+ }
+}
+
+/**
+ @short threadsafe helper for dispatch calls
+ @descr We support two interfaces for the same process - dispatch URLs. That the reason for this internal
+ function. It implements the real dispatch operation and returns a state value which inform caller
+ about success. He can notify listener then by using this return value.
+
+ @param aURL
+ uno URL which should be executed
+
+ @return <NULL/> if requested service couldn't be created successfully;
+ a valid reference otherwise. This return value can be used to indicate,
+ if dispatch was successful.
+*/
+css::uno::Reference< css::uno::XInterface > ServiceHandler::implts_dispatch( const css::util::URL& aURL )
+{
+ // extract service name and may optional given parameters from given URL
+ // and use it to create and start the component
+ OUString sServiceAndArguments = aURL.Complete.copy(PROTOCOL_VALUE.getLength());
+ OUString sServiceName;
+ OUString sArguments;
+
+ sal_Int32 nArgStart = sServiceAndArguments.indexOf('?');
+ if (nArgStart!=-1)
+ {
+ sServiceName = sServiceAndArguments.copy(0,nArgStart);
+ ++nArgStart; // ignore '?'!
+ sArguments = sServiceAndArguments.copy(nArgStart);
+ }
+ else
+ {
+ sServiceName = sServiceAndArguments;
+ }
+
+ if (sServiceName.isEmpty())
+ return css::uno::Reference< css::uno::XInterface >();
+
+ // If a service doesn't support an optional job executor interface - he can't get
+ // any given parameters!
+ // Because we can't know if we must call createInstanceWithArguments() or XJobExecutor::trigger() ...
+
+ css::uno::Reference< css::uno::XInterface > xService;
+ try
+ {
+ // => a) a service starts running inside his own ctor and we create it only
+ xService = m_xContext->getServiceManager()->createInstanceWithContext(sServiceName, m_xContext);
+ // or b) he implements the right interface and starts there (may with optional parameters)
+ css::uno::Reference< css::task::XJobExecutor > xExecutable(xService, css::uno::UNO_QUERY);
+ if (xExecutable.is())
+ xExecutable->trigger(sArguments);
+ }
+ // ignore all errors - inclusive runtime errors!
+ // E.g. a script based service (written in Python) could not be executed
+ // because it contains syntax errors, which was detected at runtime...
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("fwk.dispatch", "ignored");
+ xService.clear();
+ }
+
+ return xService;
+}
+
+/**
+ @short add/remove listener for state events
+ @descr We use an internal container to hold such registered listener. This container lives if we live.
+ And if call pass registration as non breakable transaction - we can accept the request without
+ any explicit lock. Because we share our mutex with this container.
+
+ @param xListener
+ reference to a valid listener for state events
+ @param aURL
+ URL about listener will be informed, if something occurred
+*/
+void SAL_CALL ServiceHandler::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ ,
+ const css::util::URL& /*aURL*/ )
+{
+ // not supported yet
+}
+
+void SAL_CALL ServiceHandler::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ ,
+ const css::util::URL& /*aURL*/ )
+{
+ // not supported yet
+}
+
+} // namespace framework
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_ServiceHandler_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::ServiceHandler(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/startmoduledispatcher.cxx b/framework/source/dispatch/startmoduledispatcher.cxx
new file mode 100644
index 000000000..39ac70ee1
--- /dev/null
+++ b/framework/source/dispatch/startmoduledispatcher.cxx
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/startmoduledispatcher.hxx>
+
+#include <framework/framelistanalyzer.hxx>
+#include <targets.h>
+#include "isstartmoduledispatch.hxx"
+
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/StartModule.hpp>
+
+#include <unotools/moduleoptions.hxx>
+#include <utility>
+
+namespace framework{
+
+#ifdef fpf
+ #error "Who uses \"fpf\" as define. It will overwrite my namespace alias ..."
+#endif
+
+StartModuleDispatcher::StartModuleDispatcher(css::uno::Reference< css::uno::XComponentContext > xContext)
+ : m_xContext (std::move(xContext ))
+{
+}
+
+StartModuleDispatcher::~StartModuleDispatcher()
+{
+}
+
+void SAL_CALL StartModuleDispatcher::dispatch(const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments)
+{
+ dispatchWithNotification(aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >());
+}
+
+void SAL_CALL StartModuleDispatcher::dispatchWithNotification(const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& /*lArguments*/,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ ::sal_Int16 nResult = css::frame::DispatchResultState::DONTKNOW;
+ if (isStartModuleDispatch(aURL))
+ {
+ nResult = css::frame::DispatchResultState::FAILURE;
+ if (implts_isBackingModePossible ())
+ {
+ implts_establishBackingMode ();
+ nResult = css::frame::DispatchResultState::SUCCESS;
+ }
+ }
+
+ implts_notifyResultListener(xListener, nResult, css::uno::Any());
+}
+
+css::uno::Sequence< ::sal_Int16 > SAL_CALL StartModuleDispatcher::getSupportedCommandGroups()
+{
+ return css::uno::Sequence< ::sal_Int16 >();
+}
+
+css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL StartModuleDispatcher::getConfigurableDispatchInformation(::sal_Int16 /*nCommandGroup*/)
+{
+ return css::uno::Sequence< css::frame::DispatchInformation >();
+}
+
+void SAL_CALL StartModuleDispatcher::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/,
+ const css::util::URL& /*aURL*/ )
+{
+}
+
+void SAL_CALL StartModuleDispatcher::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/,
+ const css::util::URL& /*aURL*/ )
+{
+}
+
+bool StartModuleDispatcher::implts_isBackingModePossible()
+{
+ if ( ! SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE))
+ return false;
+
+ css::uno::Reference< css::frame::XFramesSupplier > xDesktop =
+ css::frame::Desktop::create( m_xContext );
+
+ FrameListAnalyzer aCheck(
+ xDesktop,
+ css::uno::Reference< css::frame::XFrame >(),
+ FrameAnalyzerFlags::Help | FrameAnalyzerFlags::BackingComponent);
+
+ bool bIsPossible = false;
+
+ if ( ! aCheck.m_xBackingComponent.is()
+ && aCheck.m_lOtherVisibleFrames.empty() )
+ {
+ bIsPossible = true;
+ }
+
+ return bIsPossible;
+}
+
+void StartModuleDispatcher::implts_establishBackingMode()
+{
+ css::uno::Reference< css::frame::XDesktop2> xDesktop = css::frame::Desktop::create( m_xContext );
+ css::uno::Reference< css::frame::XFrame > xFrame = xDesktop->findFrame(SPECIALTARGET_BLANK, 0);
+ css::uno::Reference< css::awt::XWindow > xContainerWindow = xFrame->getContainerWindow();
+
+ css::uno::Reference< css::frame::XController > xStartModule = css::frame::StartModule::createWithParentWindow(m_xContext, xContainerWindow);
+ css::uno::Reference< css::awt::XWindow > xComponentWindow(xStartModule, css::uno::UNO_QUERY);
+ xFrame->setComponent(xComponentWindow, xStartModule);
+ xStartModule->attachFrame(xFrame);
+ xContainerWindow->setVisible(true);
+}
+
+void StartModuleDispatcher::implts_notifyResultListener(const css::uno::Reference< css::frame::XDispatchResultListener >& xListener,
+ ::sal_Int16 nState ,
+ const css::uno::Any& aResult )
+{
+ if ( ! xListener.is())
+ return;
+
+ css::frame::DispatchResultEvent aEvent(
+ css::uno::Reference< css::uno::XInterface >(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY),
+ nState,
+ aResult);
+
+ xListener->dispatchFinished(aEvent);
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/systemexec.cxx b/framework/source/dispatch/systemexec.cxx
new file mode 100644
index 000000000..c7b377842
--- /dev/null
+++ b/framework/source/dispatch/systemexec.cxx
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/systemexec.hxx>
+#include <services.h>
+
+#include <com/sun/star/system/SystemShellExecute.hpp>
+#include <com/sun/star/util/PathSubstitution.hpp>
+#include <com/sun/star/util/XStringSubstitution.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <utility>
+
+namespace framework{
+
+constexpr OUStringLiteral PROTOCOL_VALUE = u"systemexecute:";
+
+// XInterface, XTypeProvider, XServiceInfo
+
+OUString SAL_CALL SystemExec::getImplementationName()
+{
+ return "com.sun.star.comp.framework.SystemExecute";
+}
+
+sal_Bool SAL_CALL SystemExec::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL SystemExec::getSupportedServiceNames()
+{
+ return { SERVICENAME_PROTOCOLHANDLER };
+}
+
+SystemExec::SystemExec( css::uno::Reference< css::uno::XComponentContext > xContext )
+ : m_xContext (std::move( xContext ))
+{
+}
+
+SystemExec::~SystemExec()
+{
+}
+
+css::uno::Reference< css::frame::XDispatch > SAL_CALL SystemExec::queryDispatch( const css::util::URL& aURL ,
+ const OUString&,
+ sal_Int32 )
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatcher;
+ if (aURL.Complete.startsWith(PROTOCOL_VALUE))
+ xDispatcher = this;
+ return xDispatcher;
+}
+
+css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL SystemExec::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor )
+{
+ sal_Int32 nCount = lDescriptor.getLength();
+ css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatcher( nCount );
+ auto lDispatcherRange = asNonConstRange(lDispatcher);
+ for( sal_Int32 i=0; i<nCount; ++i )
+ {
+ lDispatcherRange[i] = queryDispatch(
+ lDescriptor[i].FeatureURL,
+ lDescriptor[i].FrameName,
+ lDescriptor[i].SearchFlags);
+ }
+ return lDispatcher;
+}
+
+void SAL_CALL SystemExec::dispatch( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments )
+{
+ dispatchWithNotification(aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >());
+}
+
+void SAL_CALL SystemExec::dispatchWithNotification( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >&,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ // convert "systemexec:file:///c:/temp/test.html" => "file:///c:/temp/test.html"
+ sal_Int32 c = aURL.Complete.getLength()-PROTOCOL_VALUE.getLength();
+ if (c<1) // we don't check for valid URLs here! The system will show an error message ...
+ {
+ impl_notifyResultListener(xListener, css::frame::DispatchResultState::FAILURE);
+ return;
+ }
+ OUString sSystemURLWithVariables = aURL.Complete.copy(PROTOCOL_VALUE.getLength(), c);
+
+ // TODO check security settings ...
+
+ try
+ {
+ css::uno::Reference< css::util::XStringSubstitution > xPathSubst( css::util::PathSubstitution::create(m_xContext) );
+
+ OUString sSystemURL = xPathSubst->substituteVariables(sSystemURLWithVariables, true); // sal_True force an exception if unknown variables exists !
+
+ css::uno::Reference< css::system::XSystemShellExecute > xShell = css::system::SystemShellExecute::create( m_xContext );
+
+ xShell->execute(sSystemURL, OUString(), css::system::SystemShellExecuteFlags::URIS_ONLY);
+ impl_notifyResultListener(xListener, css::frame::DispatchResultState::SUCCESS);
+ }
+ catch(const css::uno::Exception&)
+ {
+ impl_notifyResultListener(xListener, css::frame::DispatchResultState::FAILURE);
+ }
+}
+
+void SAL_CALL SystemExec::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >&,
+ const css::util::URL& )
+{
+ // not supported yet
+}
+
+void SAL_CALL SystemExec::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >&,
+ const css::util::URL& )
+{
+ // not supported yet
+}
+
+void SystemExec::impl_notifyResultListener(const css::uno::Reference< css::frame::XDispatchResultListener >& xListener,
+ const sal_Int16 nState )
+{
+ if (xListener.is())
+ {
+ css::frame::DispatchResultEvent aEvent;
+ aEvent.State = nState;
+ xListener->dispatchFinished(aEvent);
+ }
+}
+
+} // namespace framework
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+framework_SystemExecute_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new framework::SystemExec(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/dispatch/windowcommanddispatch.cxx b/framework/source/dispatch/windowcommanddispatch.cxx
new file mode 100644
index 000000000..abad29dc7
--- /dev/null
+++ b/framework/source/dispatch/windowcommanddispatch.cxx
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dispatch/windowcommanddispatch.hxx>
+#include <targets.h>
+
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+
+#include <utility>
+#include <vcl/window.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/commandevent.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+namespace framework{
+
+WindowCommandDispatch::WindowCommandDispatch(css::uno::Reference< css::uno::XComponentContext > xContext ,
+ const css::uno::Reference< css::frame::XFrame >& xFrame)
+ : m_xContext (std::move(xContext ))
+ , m_xFrame (xFrame )
+ , m_xWindow (xFrame->getContainerWindow())
+{
+ impl_startListening();
+}
+
+WindowCommandDispatch::~WindowCommandDispatch()
+{
+ impl_stopListening();
+ m_xContext.clear();
+}
+
+void WindowCommandDispatch::impl_startListening()
+{
+ std::unique_lock aReadLock(m_mutex);
+ css::uno::Reference< css::awt::XWindow > xWindow( m_xWindow.get(), css::uno::UNO_QUERY );
+ aReadLock.unlock();
+
+ if ( ! xWindow.is())
+ return;
+
+ {
+ SolarMutexGuard aSolarLock;
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow);
+ if ( ! pWindow)
+ return;
+
+ pWindow->AddEventListener( LINK(this, WindowCommandDispatch, impl_notifyCommand) );
+ }
+}
+
+void WindowCommandDispatch::impl_stopListening()
+{
+ std::unique_lock aReadLock(m_mutex);
+ css::uno::Reference< css::awt::XWindow > xWindow( m_xWindow.get(), css::uno::UNO_QUERY );
+ aReadLock.unlock();
+
+ if (!xWindow.is())
+ return;
+
+ {
+ SolarMutexGuard aSolarLock;
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xWindow);
+ if (!pWindow)
+ return;
+
+ pWindow->RemoveEventListener( LINK(this, WindowCommandDispatch, impl_notifyCommand) );
+
+ m_xWindow.clear();
+ }
+}
+
+IMPL_LINK(WindowCommandDispatch, impl_notifyCommand, VclWindowEvent&, rEvent, void)
+{
+ if (rEvent.GetId() == VclEventId::ObjectDying)
+ {
+ impl_stopListening();
+ return;
+ }
+ if (rEvent.GetId() != VclEventId::WindowCommand)
+ return;
+
+ const CommandEvent* pCommand = static_cast<CommandEvent*>(rEvent.GetData());
+ if (pCommand->GetCommand() != CommandEventId::ShowDialog)
+ return;
+
+ const CommandDialogData* pData = pCommand->GetDialogData();
+ if ( ! pData)
+ return;
+
+ const ShowDialogId nCommand = pData->GetDialogId();
+ OUString sCommand;
+
+ switch (nCommand)
+ {
+ case ShowDialogId::Preferences :
+ sCommand = ".uno:OptionsTreeDialog";
+ break;
+
+ case ShowDialogId::About :
+ sCommand = ".uno:About";
+ break;
+
+ default :
+ return;
+ }
+
+ // ignore all errors here. It's clicking a menu entry only ...
+ // The user will try it again, in case nothing happens .-)
+ try
+ {
+ // SYNCHRONIZED ->
+ std::unique_lock aReadLock(m_mutex);
+ css::uno::Reference< css::frame::XDispatchProvider > xProvider(m_xFrame.get(), css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext;
+ aReadLock.unlock();
+ // <- SYNCHRONIZED
+
+ // check provider ... we know it's weak reference only
+ if ( ! xProvider.is())
+ return;
+
+ css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(xContext));
+ css::util::URL aCommand;
+ aCommand.Complete = sCommand;
+ xParser->parseStrict(aCommand);
+
+ css::uno::Reference< css::frame::XDispatch > xDispatch = xProvider->queryDispatch(aCommand, SPECIALTARGET_SELF, 0);
+ if (xDispatch.is())
+ xDispatch->dispatch(aCommand, css::uno::Sequence< css::beans::PropertyValue >());
+ }
+ catch(const css::uno::Exception&)
+ {}
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */