/* -*- 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 #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; namespace { /// Sample interception implementation that asserts getInterceptedURLs() and queryDispatch() is in sync. class MyInterceptor : public cppu::WeakImplHelper { uno::Reference m_xMaster; uno::Reference m_xSlave; uno::Sequence m_aDisabledCommands; int m_nExpected; int m_nUnexpected; public: MyInterceptor(); /// Number of queryDispatch() calls that operate on a command advertised by getInterceptedURLs(). int getExpected(); /// Number of queryDispatch() calls that operate on a command not advertised by getInterceptedURLs(). int getUnexpected(); // frame::XInterceptorInfo virtual uno::Sequence SAL_CALL getInterceptedURLs() override; // frame::XDispatchProviderInterceptor virtual void SAL_CALL setMasterDispatchProvider( const uno::Reference& xNewSupplier) override; virtual uno::Reference SAL_CALL getMasterDispatchProvider() override; virtual void SAL_CALL setSlaveDispatchProvider(const uno::Reference& xNewSupplier) override; virtual uno::Reference SAL_CALL getSlaveDispatchProvider() override; // frame::XDispatchProvider virtual uno::Sequence> SAL_CALL queryDispatches(const uno::Sequence& rRequests) override; virtual uno::Reference SAL_CALL queryDispatch(const util::URL& rURL, const OUString& rTargetFrameName, sal_Int32 SearchFlags) override; }; MyInterceptor::MyInterceptor() : m_aDisabledCommands{ ".uno:Bold" } , m_nExpected(0) , m_nUnexpected(0) { } int MyInterceptor::getExpected() { int nRet = m_nExpected; m_nExpected = 0; return nRet; } int MyInterceptor::getUnexpected() { int nRet = m_nUnexpected; m_nUnexpected = 0; return nRet; } uno::Sequence MyInterceptor::getInterceptedURLs() { return m_aDisabledCommands; } void MyInterceptor::setMasterDispatchProvider( const uno::Reference& xNewSupplier) { m_xMaster = xNewSupplier; } uno::Reference MyInterceptor::getMasterDispatchProvider() { return m_xMaster; } void MyInterceptor::setSlaveDispatchProvider( const uno::Reference& xNewSupplier) { m_xSlave = xNewSupplier; } uno::Reference MyInterceptor::getSlaveDispatchProvider() { return m_xSlave; } uno::Sequence> MyInterceptor::queryDispatches(const uno::Sequence& rRequests) { uno::Sequence> 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; } uno::Reference MyInterceptor::queryDispatch(const util::URL& rURL, const OUString& /*rTargetFrameName*/, sal_Int32 /*SearchFlags*/) { if (std::find(std::cbegin(m_aDisabledCommands), std::cend(m_aDisabledCommands), rURL.Complete) != std::cend(m_aDisabledCommands)) ++m_nExpected; else ++m_nUnexpected; return uno::Reference(); } /// Tests how InterceptionHelper invokes a registered interceptor. class DispatchTest : public test::BootstrapFixture, public unotest::MacrosTest { protected: uno::Reference mxComponent; public: virtual void setUp() override; virtual void tearDown() override; }; void DispatchTest::setUp() { test::BootstrapFixture::setUp(); mxDesktop.set(frame::Desktop::create(mxComponentContext)); } void DispatchTest::tearDown() { if (mxComponent.is()) mxComponent->dispose(); test::BootstrapFixture::tearDown(); } CPPUNIT_TEST_FIXTURE(DispatchTest, testInterception) { mxComponent = loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument"); uno::Reference xModel(mxComponent, uno::UNO_QUERY); CPPUNIT_ASSERT(xModel.is()); uno::Reference xRegistration( xModel->getCurrentController()->getFrame(), uno::UNO_QUERY); CPPUNIT_ASSERT(xRegistration.is()); rtl::Reference pInterceptor(new MyInterceptor()); xRegistration->registerDispatchProviderInterceptor(pInterceptor); dispatchCommand(mxComponent, ".uno:Bold", {}); CPPUNIT_ASSERT_EQUAL(1, pInterceptor->getExpected()); CPPUNIT_ASSERT_EQUAL(0, pInterceptor->getUnexpected()); dispatchCommand(mxComponent, ".uno:Italic", {}); CPPUNIT_ASSERT_EQUAL(1, pInterceptor->getExpected()); // This was 1: MyInterceptor::queryDispatch() was called for .uno:Italic. CPPUNIT_ASSERT_EQUAL(0, pInterceptor->getUnexpected()); } constexpr OUStringLiteral DATA_DIRECTORY = u"/framework/qa/cppunit/data/"; CPPUNIT_TEST_FIXTURE(DispatchTest, testSfxOfficeDispatchDispose) { // this test doesn't work with a new document because of aURL.Main check in SfxBaseController::dispatch() mxComponent = loadFromDesktop(m_directories.getURLFromSrc(DATA_DIRECTORY) + "empty.fodp", "com.sun.star.presentation.PresentationDocument"); uno::Reference xModel(mxComponent, uno::UNO_QUERY); CPPUNIT_ASSERT(xModel.is()); uno::Reference xController(xModel->getCurrentController()); CPPUNIT_ASSERT(xController.is()); uno::Reference xFrame(xController->getFrame(), uno::UNO_QUERY); CPPUNIT_ASSERT(xFrame.is()); uno::Reference xParser(util::URLTransformer::create(mxComponentContext)); util::URL url; url.Complete = xModel->getURL() + "#dummy"; xParser->parseStrict(url); uno::Reference xDisp(xFrame->queryDispatch(url, "", 0)); CPPUNIT_ASSERT(xDisp.is()); mxComponent->dispose(); util::URL urlSlot; urlSlot.Complete = "slot:5598"; xParser->parseStrict(urlSlot); // crashed with UAF xDisp->dispatch(urlSlot, {}); } } CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */