1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <loadenv/loadenv.hxx>
22 #include <loadenv/loadenvexception.hxx>
23 #include <loadenv/targethelper.hxx>
24 #include <framework/framelistanalyzer.hxx>
26 #include <interaction/quietinteraction.hxx>
27 #include <properties.h>
28 #include <protocols.h>
31 #include <comphelper/interaction.hxx>
32 #include <comphelper/lok.hxx>
33 #include <comphelper/namedvaluecollection.hxx>
34 #include <comphelper/propertysequence.hxx>
35 #include <framework/interaction.hxx>
36 #include <comphelper/processfactory.hxx>
37 #include <officecfg/Office/Common.hxx>
38 #include <officecfg/Setup.hxx>
40 #include <com/sun/star/awt/XWindow2.hpp>
41 #include <com/sun/star/beans/XPropertySet.hpp>
42 #include <com/sun/star/container/XNameAccess.hpp>
43 #include <com/sun/star/container/XEnumeration.hpp>
44 #include <com/sun/star/document/MacroExecMode.hpp>
45 #include <com/sun/star/document/XTypeDetection.hpp>
46 #include <com/sun/star/document/XActionLockable.hpp>
47 #include <com/sun/star/document/UpdateDocMode.hpp>
48 #include <com/sun/star/frame/Desktop.hpp>
49 #include <com/sun/star/frame/OfficeFrameLoader.hpp>
50 #include <com/sun/star/frame/XModel.hpp>
51 #include <com/sun/star/frame/XFrameLoader.hpp>
52 #include <com/sun/star/frame/XSynchronousFrameLoader.hpp>
53 #include <com/sun/star/frame/XNotifyingDispatch.hpp>
54 #include <com/sun/star/frame/FrameLoaderFactory.hpp>
55 #include <com/sun/star/frame/ContentHandlerFactory.hpp>
56 #include <com/sun/star/frame/DispatchResultState.hpp>
57 #include <com/sun/star/frame/FrameSearchFlag.hpp>
58 #include <com/sun/star/frame/XDispatchProvider.hpp>
59 #include <com/sun/star/lang/IllegalArgumentException.hpp>
60 #include <com/sun/star/lang/XInitialization.hpp>
61 #include <com/sun/star/lang/DisposedException.hpp>
62 #include <com/sun/star/io/XInputStream.hpp>
63 #include <com/sun/star/task/XInteractionHandler.hpp>
64 #include <com/sun/star/task/ErrorCodeRequest.hpp>
65 #include <com/sun/star/task/InteractionHandler.hpp>
66 #include <com/sun/star/task/XStatusIndicatorFactory.hpp>
67 #include <com/sun/star/task/XStatusIndicator.hpp>
68 #include <com/sun/star/uno/RuntimeException.hpp>
69 #include <com/sun/star/ucb/UniversalContentBroker.hpp>
70 #include <com/sun/star/util/CloseVetoException.hpp>
71 #include <com/sun/star/util/URLTransformer.hpp>
72 #include <com/sun/star/util/XURLTransformer.hpp>
73 #include <com/sun/star/util/XCloseable.hpp>
74 #include <com/sun/star/util/XModifiable.hpp>
77 #include <vcl/window.hxx>
78 #include <vcl/wrkwin.hxx>
79 #include <vcl/syswin.hxx>
81 #include <toolkit/helper/vclunohelper.hxx>
82 #include <unotools/moduleoptions.hxx>
83 #include <svtools/sfxecode.hxx>
84 #include <unotools/ucbhelper.hxx>
85 #include <comphelper/configurationhelper.hxx>
86 #include <rtl/bootstrap.hxx>
87 #include <sal/log.hxx>
88 #include <comphelper/errcode.hxx>
89 #include <vcl/svapp.hxx>
90 #include <cppuhelper/implbase.hxx>
91 #include <comphelper/profilezone.hxx>
92 #include <classes/taskcreator.hxx>
93 #include <tools/fileutil.hxx>
95 constexpr OUString PROP_TYPES
= u
"Types"_ustr
;
96 constexpr OUString PROP_NAME
= u
"Name"_ustr
;
100 using namespace com::sun::star
;
104 class LoadEnvListener
: public ::cppu::WeakImplHelper
< css::frame::XLoadEventListener
,
105 css::frame::XDispatchResultListener
>
109 bool m_bWaitingResult
;
114 explicit LoadEnvListener(LoadEnv
* pLoadEnv
)
115 : m_bWaitingResult(true)
116 , m_pLoadEnv(pLoadEnv
)
120 // frame.XLoadEventListener
121 virtual void SAL_CALL
loadFinished(const css::uno::Reference
< css::frame::XFrameLoader
>& xLoader
) override
;
123 virtual void SAL_CALL
loadCancelled(const css::uno::Reference
< css::frame::XFrameLoader
>& xLoader
) override
;
125 // frame.XDispatchResultListener
126 virtual void SAL_CALL
dispatchFinished(const css::frame::DispatchResultEvent
& aEvent
) override
;
128 // lang.XEventListener
129 virtual void SAL_CALL
disposing(const css::lang::EventObject
& aEvent
) override
;
134 LoadEnv::LoadEnv(css::uno::Reference
< css::uno::XComponentContext
> xContext
)
135 : m_xContext(std::move(xContext
))
137 , m_eFeature(LoadEnvFeatures::NONE
)
138 , m_eContentType(E_UNSUPPORTED_CONTENT
)
139 , m_bCloseFrameOnError(false)
140 , m_bReactivateControllerOnError(false)
149 css::uno::Reference
< css::lang::XComponent
> LoadEnv::loadComponentFromURL(const css::uno::Reference
< css::frame::XComponentLoader
>& xLoader
,
150 const css::uno::Reference
< css::uno::XComponentContext
>& xContext
,
151 const OUString
& sURL
,
152 const OUString
& sTarget
,
153 sal_Int32 nSearchFlags
,
154 const css::uno::Sequence
< css::beans::PropertyValue
>& lArgs
)
156 css::uno::Reference
< css::lang::XComponent
> xComponent
;
157 comphelper::ProfileZone
aZone("loadComponentFromURL");
161 LoadEnv
aEnv(xContext
);
162 LoadEnvFeatures loadEnvFeatures
= LoadEnvFeatures::WorkWithUI
;
163 // tdf#118238 Only disable UI interaction when loading as hidden
164 if (comphelper::NamedValueCollection::get(lArgs
, u
"Hidden") == uno::Any(true) || Application::IsHeadlessModeEnabled())
165 loadEnvFeatures
= LoadEnvFeatures::NONE
;
167 aEnv
.startLoading(sURL
,
169 css::uno::Reference
< css::frame::XFrame
>(xLoader
, css::uno::UNO_QUERY
),
173 aEnv
.waitWhileLoading(); // wait for ever!
175 xComponent
= aEnv
.getTargetComponent();
177 catch(const LoadEnvException
& ex
)
181 case LoadEnvException::ID_INVALID_MEDIADESCRIPTOR
:
182 throw css::lang::IllegalArgumentException(
183 u
"Optional list of arguments seem to be corrupted."_ustr
, xLoader
, 4);
185 case LoadEnvException::ID_UNSUPPORTED_CONTENT
:
186 throw css::lang::IllegalArgumentException(
187 "Unsupported URL <" + sURL
+ ">: \"" + ex
.m_sMessage
+ "\"",
193 "caught LoadEnvException " << +ex
.m_nID
<< " \""
194 << ex
.m_sMessage
<< "\""
195 << (ex
.m_exOriginal
.has
<css::uno::Exception
>()
196 ? (", " + ex
.m_exOriginal
.getValueTypeName() + " \""
197 + (ex
.m_exOriginal
.get
<css::uno::Exception
>().
201 << " while loading <" << sURL
<< ">");
207 // if AbortOnLoadFailure is set and we couldn't load the document, assert, intended for use with crashtesting to
208 // detect when we export something we can't import
209 assert(xComponent
.is() || comphelper::NamedValueCollection::get(lArgs
, u
"AbortOnLoadFailure") != uno::Any(true));
216 utl::MediaDescriptor
addModelArgs(const uno::Sequence
<beans::PropertyValue
>& rDescriptor
)
218 utl::MediaDescriptor
rResult(rDescriptor
);
219 uno::Reference
<frame::XModel
> xModel(rResult
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_MODEL
, uno::Reference
<frame::XModel
>()));
223 utl::MediaDescriptor
aModelArgs(xModel
->getArgs());
224 utl::MediaDescriptor::iterator pIt
= aModelArgs
.find( utl::MediaDescriptor::PROP_MACROEXECUTIONMODE
);
225 if (pIt
!= aModelArgs
.end())
226 rResult
[utl::MediaDescriptor::PROP_MACROEXECUTIONMODE
] = pIt
->second
;
234 void LoadEnv::startLoading(const OUString
& sURL
, const uno::Sequence
<beans::PropertyValue
>& lMediaDescriptor
,
235 const uno::Reference
<frame::XFrame
>& xBaseFrame
, const OUString
& sTarget
,
236 sal_Int32 nSearchFlags
, LoadEnvFeatures eFeature
)
238 osl::MutexGuard
g(m_mutex
);
240 // Handle still running processes!
241 if (m_xAsynchronousJob
.is())
242 throw LoadEnvException(LoadEnvException::ID_STILL_RUNNING
);
244 // take over all new parameters.
245 m_xTargetFrame
.clear();
246 m_xBaseFrame
= xBaseFrame
;
247 m_lMediaDescriptor
= addModelArgs(lMediaDescriptor
);
249 m_nSearchFlags
= nSearchFlags
;
250 m_eFeature
= eFeature
;
251 m_eContentType
= E_UNSUPPORTED_CONTENT
;
252 m_bCloseFrameOnError
= false;
253 m_bReactivateControllerOnError
= false;
257 if (!officecfg::Office::Common::Load::DetectWebDAVRedirection::get()
258 || !tools::IsMappedWebDAVPath(sURL
, &aRealURL
))
261 // try to find out, if it's really a content, which can be loaded or must be "handled"
262 // We use a default value for this in-parameter. Then we have to start a complex check method
263 // internally. But if this check was already done outside it can be suppressed to perform
264 // the load request. We take over the result then!
265 m_eContentType
= LoadEnv::classifyContent(aRealURL
, lMediaDescriptor
);
266 if (m_eContentType
== E_UNSUPPORTED_CONTENT
)
267 throw LoadEnvException(LoadEnvException::ID_UNSUPPORTED_CONTENT
, u
"from LoadEnv::startLoading"_ustr
);
269 // make URL part of the MediaDescriptor
270 // It doesn't matter if it is already an item of it.
271 // It must be the same value... so we can overwrite it :-)
272 m_lMediaDescriptor
[utl::MediaDescriptor::PROP_URL
] <<= aRealURL
;
274 // parse it - because some following code require that
275 m_aURL
.Complete
= aRealURL
;
276 uno::Reference
<util::XURLTransformer
> xParser(util::URLTransformer::create(m_xContext
));
277 xParser
->parseStrict(m_aURL
);
279 // BTW: Split URL and JumpMark ...
280 // Because such mark is an explicit value of the media descriptor!
281 if (!m_aURL
.Mark
.isEmpty())
282 m_lMediaDescriptor
[utl::MediaDescriptor::PROP_JUMPMARK
] <<= m_aURL
.Mark
;
284 // By the way: remove the old and deprecated value "FileName" from the descriptor!
285 utl::MediaDescriptor::iterator pIt
= m_lMediaDescriptor
.find(utl::MediaDescriptor::PROP_FILENAME
);
286 if (pIt
!= m_lMediaDescriptor
.end())
287 m_lMediaDescriptor
.erase(pIt
);
289 // patch the MediaDescriptor, so it fulfil the outside requirements
290 // Means especially items like e.g. UI InteractionHandler, Status Indicator,
291 // MacroExecutionMode, etc.
293 /*TODO progress is bound to a frame ... How can we set it here? */
297 (m_eFeature
& LoadEnvFeatures::WorkWithUI
) &&
298 !m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN
, false) &&
299 !m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_PREVIEW
, false);
301 if( comphelper::LibreOfficeKit::isActive() &&
302 m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_SILENT
, false))
304 rtl::Reference
<QuietInteraction
> pQuietInteraction
= new QuietInteraction();
305 uno::Reference
<task::XInteractionHandler
> xInteractionHandler(pQuietInteraction
);
306 m_lMediaDescriptor
[utl::MediaDescriptor::PROP_INTERACTIONHANDLER
] <<= xInteractionHandler
;
309 initializeUIDefaults(m_xContext
, m_lMediaDescriptor
, bUIMode
, &m_pQuietInteraction
);
314 void LoadEnv::initializeUIDefaults( const css::uno::Reference
< css::uno::XComponentContext
>& i_rxContext
,
315 utl::MediaDescriptor
& io_lMediaDescriptor
, const bool i_bUIMode
,
316 rtl::Reference
<QuietInteraction
>* o_ppQuietInteraction
)
318 css::uno::Reference
< css::task::XInteractionHandler
> xInteractionHandler
;
319 sal_Int16 nMacroMode
;
320 sal_Int16 nUpdateMode
;
324 nMacroMode
= css::document::MacroExecMode::USE_CONFIG
;
325 nUpdateMode
= css::document::UpdateDocMode::ACCORDING_TO_CONFIG
;
328 // tdf#154308 At least for the case the document is launched from the StartCenter, put that StartCenter as the
329 // parent for any dialogs that may appear during typedetection (once load starts a permanent frame will be set
330 // anyway and used as dialog parent, which will be this one if the startcenter was running)
331 css::uno::Reference
<css::frame::XFramesSupplier
> xSupplier
= css::frame::Desktop::create(i_rxContext
);
332 FrameListAnalyzer
aTasksAnalyzer(xSupplier
, css::uno::Reference
<css::frame::XFrame
>(), FrameAnalyzerFlags::BackingComponent
);
333 css::uno::Reference
<css::awt::XWindow
> xDialogParent(aTasksAnalyzer
.m_xBackingComponent
?
334 aTasksAnalyzer
.m_xBackingComponent
->getContainerWindow() :
337 xInteractionHandler
.set( css::task::InteractionHandler::createWithParent(i_rxContext
, xDialogParent
), css::uno::UNO_QUERY_THROW
);
339 catch(const css::uno::RuntimeException
&) {throw;}
340 catch(const css::uno::Exception
& ) { }
345 nMacroMode
= css::document::MacroExecMode::NEVER_EXECUTE
;
346 nUpdateMode
= css::document::UpdateDocMode::NO_UPDATE
;
347 rtl::Reference
<QuietInteraction
> pQuietInteraction
= new QuietInteraction();
348 xInteractionHandler
= pQuietInteraction
.get();
349 if ( o_ppQuietInteraction
!= nullptr )
351 *o_ppQuietInteraction
= std::move(pQuietInteraction
);
355 if ( xInteractionHandler
.is() )
357 if( io_lMediaDescriptor
.find(utl::MediaDescriptor::PROP_INTERACTIONHANDLER
) == io_lMediaDescriptor
.end() )
359 io_lMediaDescriptor
[utl::MediaDescriptor::PROP_INTERACTIONHANDLER
] <<= xInteractionHandler
;
361 if( io_lMediaDescriptor
.find(utl::MediaDescriptor::PROP_AUTHENTICATIONHANDLER
) == io_lMediaDescriptor
.end() )
363 io_lMediaDescriptor
[utl::MediaDescriptor::PROP_AUTHENTICATIONHANDLER
] <<= xInteractionHandler
;
367 if (io_lMediaDescriptor
.find(utl::MediaDescriptor::PROP_MACROEXECUTIONMODE
) == io_lMediaDescriptor
.end())
368 io_lMediaDescriptor
[utl::MediaDescriptor::PROP_MACROEXECUTIONMODE
] <<= nMacroMode
;
370 if (io_lMediaDescriptor
.find(utl::MediaDescriptor::PROP_UPDATEDOCMODE
) == io_lMediaDescriptor
.end())
371 io_lMediaDescriptor
[utl::MediaDescriptor::PROP_UPDATEDOCMODE
] <<= nUpdateMode
;
374 void LoadEnv::start()
378 osl::MutexGuard
aReadLock(m_mutex
);
380 // Handle still running processes!
381 if (m_xAsynchronousJob
.is())
382 throw LoadEnvException(LoadEnvException::ID_STILL_RUNNING
);
384 // content can not be loaded or handled
385 // check "classifyContent()" failed before ...
386 if (m_eContentType
== E_UNSUPPORTED_CONTENT
)
387 throw LoadEnvException(LoadEnvException::ID_UNSUPPORTED_CONTENT
,
388 u
"from LoadEnv::start"_ustr
);
392 // detect its type/filter etc.
393 // This information will be available by the
394 // used descriptor member afterwards and is needed
395 // for all following operations!
396 // Note: An exception will be thrown, in case operation was not successfully ...
397 if (m_eContentType
!= E_CAN_BE_SET
)/* Attention: special feature to set existing component on a frame must ignore type detection! */
398 impl_detectTypeAndFilter();
400 // start loading the content...
401 // Attention: Don't check m_eContentType deeper then UNSUPPORTED/SUPPORTED!
402 // Because it was made in the easiest way... may a flat detection was made only.
403 // And such simple detection can fail sometimes .-)
404 // Use another strategy here. Try it and let it run into the case "loading not possible".
405 bool bStarted
= false;
407 (m_eFeature
& LoadEnvFeatures::AllowContentHandler
) &&
408 (m_eContentType
!= E_CAN_BE_SET
) /* Attention: special feature to set existing component on a frame must ignore type detection! */
411 bStarted
= impl_handleContent();
415 bStarted
= impl_loadContent();
417 // not started => general error
418 // We can't say - what was the reason for.
420 throw LoadEnvException(
421 LoadEnvException::ID_GENERAL_ERROR
, u
"not started"_ustr
);
424 /*-----------------------------------------------
426 First draft does not implement timeout using [ms].
427 Current implementation counts yield calls only ...
428 -----------------------------------------------*/
429 bool LoadEnv::waitWhileLoading(sal_uInt32 nTimeout
)
431 // Because it's not a good idea to block the main thread
432 // (and we can't be sure that we are currently not used inside the
433 // main thread!), we can't use conditions here really. We must yield
434 // in an intelligent manner :-)
436 sal_Int32 nTime
= nTimeout
;
437 while(!Application::IsQuit())
439 // SAFE -> ------------------------------
441 osl::MutexGuard
aReadLock1(m_mutex
);
442 if (!m_xAsynchronousJob
.is())
445 // <- SAFE ------------------------------
447 Application::Yield();
459 osl::MutexGuard
g(m_mutex
);
460 return !m_xAsynchronousJob
.is();
463 css::uno::Reference
< css::lang::XComponent
> LoadEnv::getTargetComponent() const
465 osl::MutexGuard
g(m_mutex
);
467 if (!m_xTargetFrame
.is())
468 return css::uno::Reference
< css::lang::XComponent
>();
470 css::uno::Reference
< css::frame::XController
> xController
= m_xTargetFrame
->getController();
471 if (!xController
.is())
472 return m_xTargetFrame
->getComponentWindow();
474 css::uno::Reference
< css::frame::XModel
> xModel
= xController
->getModel();
481 void SAL_CALL
LoadEnvListener::loadFinished(const css::uno::Reference
< css::frame::XFrameLoader
>&)
483 std::unique_lock
g(m_mutex
);
484 if (m_bWaitingResult
)
485 m_pLoadEnv
->impl_setResult(true);
486 m_bWaitingResult
= false;
489 void SAL_CALL
LoadEnvListener::loadCancelled(const css::uno::Reference
< css::frame::XFrameLoader
>&)
491 std::unique_lock
g(m_mutex
);
492 if (m_bWaitingResult
)
493 m_pLoadEnv
->impl_setResult(false);
494 m_bWaitingResult
= false;
497 void SAL_CALL
LoadEnvListener::dispatchFinished(const css::frame::DispatchResultEvent
& aEvent
)
499 std::unique_lock
g(m_mutex
);
501 if (!m_bWaitingResult
)
506 case css::frame::DispatchResultState::FAILURE
:
507 m_pLoadEnv
->impl_setResult(false);
510 case css::frame::DispatchResultState::SUCCESS
:
511 m_pLoadEnv
->impl_setResult(false);
514 case css::frame::DispatchResultState::DONTKNOW
:
515 m_pLoadEnv
->impl_setResult(false);
518 m_bWaitingResult
= false;
521 void SAL_CALL
LoadEnvListener::disposing(const css::lang::EventObject
&)
523 std::unique_lock
g(m_mutex
);
524 if (m_bWaitingResult
)
525 m_pLoadEnv
->impl_setResult(false);
526 m_bWaitingResult
= false;
529 void LoadEnv::impl_setResult(bool bResult
)
531 osl::MutexGuard
g(m_mutex
);
535 impl_reactForLoadingState();
537 // clearing of this reference will unblock waitWhileLoading()!
538 // So we must be sure, that loading process was really finished.
539 // => do it as last operation of this method ...
540 m_xAsynchronousJob
.clear();
543 /*-----------------------------------------------
544 TODO: Is it a good idea to change Sequence<>
545 parameter to stl-adapter?
546 -----------------------------------------------*/
547 LoadEnv::EContentType
LoadEnv::classifyContent(const OUString
& sURL
,
548 const css::uno::Sequence
< css::beans::PropertyValue
>& lMediaDescriptor
)
551 // (i) Filter some special well known URL protocols,
552 // which can not be handled or loaded in general.
553 // Of course an empty URL must be ignored here too.
554 // Note: These URL schemata are fix and well known ...
555 // But there can be some additional ones, which was not
556 // defined at implementation time of this class :-(
557 // So we have to make sure, that the following code
558 // can detect such protocol schemata too :-)
562 (ProtocolCheck::isProtocol(sURL
,EProtocol::Uno
)) ||
563 (ProtocolCheck::isProtocol(sURL
,EProtocol::Slot
)) ||
564 (ProtocolCheck::isProtocol(sURL
,EProtocol::Macro
)) ||
565 (ProtocolCheck::isProtocol(sURL
,EProtocol::Service
)) ||
566 (ProtocolCheck::isProtocol(sURL
,EProtocol::MailTo
)) ||
567 (ProtocolCheck::isProtocol(sURL
,EProtocol::News
))
570 return E_UNSUPPORTED_CONTENT
;
573 // (ii) Some special URLs indicates a given input stream,
574 // a full featured document model directly or
575 // specify a request for opening an empty document.
576 // Such contents are loadable in general.
577 // But we have to check, if the media descriptor contains
578 // all needed resources. If they are missing - the following
579 // load request will fail.
581 /* Attention: The following code can't work on such special URLs!
582 It should not break the office... but it makes no sense
583 to start expensive object creations and complex search
584 algorithm if it's clear, that such URLs must be handled
588 // creation of new documents
589 if (ProtocolCheck::isProtocol(sURL
,EProtocol::PrivateFactory
))
590 return E_CAN_BE_LOADED
;
592 // using of an existing input stream
593 utl::MediaDescriptor
stlMediaDescriptor(lMediaDescriptor
);
594 utl::MediaDescriptor::const_iterator pIt
;
595 if (ProtocolCheck::isProtocol(sURL
,EProtocol::PrivateStream
))
597 pIt
= stlMediaDescriptor
.find(utl::MediaDescriptor::PROP_INPUTSTREAM
);
598 css::uno::Reference
< css::io::XInputStream
> xStream
;
599 if (pIt
!= stlMediaDescriptor
.end())
600 pIt
->second
>>= xStream
;
602 return E_CAN_BE_LOADED
;
603 SAL_INFO("fwk.loadenv", "LoadEnv::classifyContent(): loading from stream with right URL but invalid stream detected");
604 return E_UNSUPPORTED_CONTENT
;
607 // using of a full featured document
608 if (ProtocolCheck::isProtocol(sURL
,EProtocol::PrivateObject
))
610 pIt
= stlMediaDescriptor
.find(utl::MediaDescriptor::PROP_MODEL
);
611 css::uno::Reference
< css::frame::XModel
> xModel
;
612 if (pIt
!= stlMediaDescriptor
.end())
613 pIt
->second
>>= xModel
;
616 SAL_INFO("fwk.loadenv", "LoadEnv::classifyContent(): loading with object with right URL but invalid object detected");
617 return E_UNSUPPORTED_CONTENT
;
620 // following operations can work on an internal type name only :-(
621 const css::uno::Reference
< css::uno::XComponentContext
>& xContext
= ::comphelper::getProcessComponentContext();
622 css::uno::Reference
< css::document::XTypeDetection
> xDetect(
623 xContext
->getServiceManager()->createInstanceWithContext(
624 u
"com.sun.star.document.TypeDetection"_ustr
, xContext
),
625 css::uno::UNO_QUERY_THROW
);
627 OUString sType
= xDetect
->queryTypeByURL(sURL
);
629 css::uno::Reference
< css::frame::XLoaderFactory
> xLoaderFactory
;
630 css::uno::Reference
< css::container::XEnumeration
> xSet
;
632 // (iii) If a FrameLoader service (or at least
633 // a Filter) can be found, which supports
634 // this URL - it must be a loadable content.
635 // Because both items are registered for types
636 // it's enough to check for frame loaders only.
637 // Most of our filters are handled by our global
638 // default loader. But there exist some specialized
639 // loader, which does not work on top of filters!
640 // So it's not enough to search on the filter configuration.
641 // Further it's not enough to search for types!
642 // Because there exist some types, which are referenced by
643 // other objects... but neither by filters nor frame loaders!
644 css::uno::Sequence
< OUString
> lTypesReg
{ sType
};
645 css::uno::Sequence
< css::beans::NamedValue
> lQuery
647 css::beans::NamedValue(PROP_TYPES
, css::uno::Any(lTypesReg
))
650 xLoaderFactory
= css::frame::FrameLoaderFactory::create(xContext
);
651 xSet
= xLoaderFactory
->createSubSetEnumerationByProperties(lQuery
);
652 // at least one registered frame loader is enough!
653 if (xSet
->hasMoreElements())
654 return E_CAN_BE_LOADED
;
656 // (iv) Some URL protocols are supported by special services.
657 // E.g. ContentHandler.
658 // Such contents can be handled ... but not loaded.
660 xLoaderFactory
= css::frame::ContentHandlerFactory::create(xContext
);
661 xSet
= xLoaderFactory
->createSubSetEnumerationByProperties(lQuery
);
662 // at least one registered content handler is enough!
663 if (xSet
->hasMoreElements())
664 return E_CAN_BE_HANDLED
;
666 // (v) Last but not least the UCB is used inside office to
667 // load contents. He has a special configuration to know
668 // which URL schemata can be used inside office.
669 css::uno::Reference
< css::ucb::XUniversalContentBroker
> xUCB(css::ucb::UniversalContentBroker::create(xContext
));
670 if (xUCB
->queryContentProvider(sURL
).is())
671 return E_CAN_BE_LOADED
;
673 // (TODO) At this point, we have no idea .-)
674 // But it seems to be better, to break all
675 // further requests for this URL. Otherwise
676 // we can run into some trouble.
677 return E_UNSUPPORTED_CONTENT
;
682 bool queryOrcusTypeAndFilter(const uno::Sequence
<beans::PropertyValue
>& rDescriptor
, OUString
& rType
, OUString
& rFilter
)
685 sal_Int32 nSize
= rDescriptor
.getLength();
686 for (sal_Int32 i
= 0; i
< nSize
; ++i
)
688 const beans::PropertyValue
& rProp
= rDescriptor
[i
];
689 if (rProp
.Name
== "URL")
691 rProp
.Value
>>= aURL
;
696 if (aURL
.isEmpty() || o3tl::equalsIgnoreAsciiCase(aURL
.subView(0,8), u
"private:"))
699 // TODO : Type must be set to be generic_Text (or any other type that
700 // exists) in order to find a usable loader. Exploit it as a temporary
703 // depending on the experimental mode
704 if (!officecfg::Office::Common::Misc::ExperimentalMode::get())
710 rtl::Bootstrap::get(u
"LIBO_USE_ORCUS"_ustr
, aUseOrcus
);
711 bool bUseOrcus
= (aUseOrcus
== "YES");
716 if (aURL
.endsWith(".xlsx"))
718 rType
= "generic_Text";
722 else if (aURL
.endsWith(".ods"))
724 rType
= "generic_Text";
728 else if (aURL
.endsWith(".csv"))
730 rType
= "generic_Text";
740 void LoadEnv::impl_detectTypeAndFilter()
742 static const sal_Int32 FILTERFLAG_TEMPLATEPATH
= 16;
745 osl::ClearableMutexGuard
aReadLock(m_mutex
);
747 // Attention: Because our stl media descriptor is a copy of a uno sequence
748 // we can't use as an in/out parameter here. Copy it before and don't forget to
749 // update structure afterwards again!
750 css::uno::Sequence
< css::beans::PropertyValue
> lDescriptor
= m_lMediaDescriptor
.getAsConstPropertyValueList();
751 css::uno::Reference
< css::uno::XComponentContext
> xContext
= m_xContext
;
756 OUString sType
, sFilter
;
758 if (queryOrcusTypeAndFilter(lDescriptor
, sType
, sFilter
) && !sType
.isEmpty() && !sFilter
.isEmpty())
761 osl::MutexGuard
aWriteLock(m_mutex
);
763 // Orcus type detected. Skip the normal type detection process.
764 m_lMediaDescriptor
<< lDescriptor
;
765 m_lMediaDescriptor
[utl::MediaDescriptor::PROP_TYPENAME
] <<= sType
;
766 m_lMediaDescriptor
[utl::MediaDescriptor::PROP_FILTERNAME
] <<= sFilter
;
767 m_lMediaDescriptor
[utl::MediaDescriptor::PROP_FILTERPROVIDER
] <<= u
"orcus"_ustr
;
768 m_lMediaDescriptor
[utl::MediaDescriptor::PROP_DOCUMENTSERVICE
] <<= u
"com.sun.star.sheet.SpreadsheetDocument"_ustr
;
773 css::uno::Reference
< css::document::XTypeDetection
> xDetect(
774 xContext
->getServiceManager()->createInstanceWithContext(
775 u
"com.sun.star.document.TypeDetection"_ustr
, xContext
),
776 css::uno::UNO_QUERY_THROW
);
777 sType
= xDetect
->queryTypeByDescriptor(lDescriptor
, true); /*TODO should deep detection be able for enable/disable it from outside? */
779 // no valid content -> loading not possible
781 throw LoadEnvException(
782 LoadEnvException::ID_UNSUPPORTED_CONTENT
, u
"type detection failed"_ustr
);
785 osl::ResettableMutexGuard
aWriteLock(m_mutex
);
787 // detection was successful => update the descriptor member of this class
788 m_lMediaDescriptor
<< lDescriptor
;
789 m_lMediaDescriptor
[utl::MediaDescriptor::PROP_TYPENAME
] <<= sType
;
790 // Is there an already detected (may be preselected) filter?
792 sFilter
= m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_FILTERNAME
, OUString());
797 // We do have potentially correct type, but the detection process was aborted.
798 if (m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_ABORTED
, false))
799 throw LoadEnvException(
800 LoadEnvException::ID_UNSUPPORTED_CONTENT
, u
"type detection aborted"_ustr
);
802 // But the type isn't enough. For loading sometimes we need more information.
803 // E.g. for our "_default" feature, where we recycle any frame which contains
804 // and "Untitled" document, we must know if the new document is based on a template!
805 // But this information is available as a filter property only.
806 // => We must try(!) to detect the right filter for this load request.
807 // On the other side ... if no filter is available .. ignore it.
808 // Then the type information must be enough.
809 if (sFilter
.isEmpty())
811 // no -> try to find a preferred filter for the detected type.
812 // Don't forget to update the media descriptor.
813 css::uno::Reference
< css::container::XNameAccess
> xTypeCont(xDetect
, css::uno::UNO_QUERY_THROW
);
816 ::comphelper::SequenceAsHashMap
lTypeProps(xTypeCont
->getByName(sType
));
817 sFilter
= lTypeProps
.getUnpackedValueOrDefault(u
"PreferredFilter"_ustr
, OUString());
818 if (!sFilter
.isEmpty())
822 m_lMediaDescriptor
[utl::MediaDescriptor::PROP_FILTERNAME
] <<= sFilter
;
827 catch(const css::container::NoSuchElementException
&)
831 // check if the filter (if one exists) points to a template format filter.
832 // Then we have to add the property "AsTemplate".
833 // We need this information to decide afterwards if we can use a "recycle frame"
834 // for target "_default" or has to create a new one every time.
835 // On the other side we have to suppress that, if this property already exists
836 // and should trigger a special handling. Then the outside call of this method here,
837 // has to know, what he is doing .-)
839 bool bIsOwnTemplate
= false;
840 if (!sFilter
.isEmpty())
842 css::uno::Reference
< css::container::XNameAccess
> xFilterCont(xContext
->getServiceManager()->createInstanceWithContext(SERVICENAME_FILTERFACTORY
, xContext
), css::uno::UNO_QUERY_THROW
);
845 ::comphelper::SequenceAsHashMap
lFilterProps(xFilterCont
->getByName(sFilter
));
846 sal_Int32 nFlags
= lFilterProps
.getUnpackedValueOrDefault(u
"Flags"_ustr
, sal_Int32(0));
847 bIsOwnTemplate
= ((nFlags
& FILTERFLAG_TEMPLATEPATH
) == FILTERFLAG_TEMPLATEPATH
);
849 catch(const css::container::NoSuchElementException
&)
856 // Don't overwrite external decisions! See comments before ...
857 utl::MediaDescriptor::const_iterator pAsTemplateItem
= m_lMediaDescriptor
.find(utl::MediaDescriptor::PROP_ASTEMPLATE
);
858 if (pAsTemplateItem
== m_lMediaDescriptor
.end())
859 m_lMediaDescriptor
[utl::MediaDescriptor::PROP_ASTEMPLATE
] <<= true;
865 bool LoadEnv::impl_handleContent()
867 // SAFE -> -----------------------------------
868 osl::ClearableMutexGuard
aReadLock(m_mutex
);
870 // the type must exist inside the descriptor ... otherwise this class is implemented wrong :-)
871 OUString sType
= m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_TYPENAME
, OUString());
873 throw LoadEnvException(LoadEnvException::ID_INVALID_MEDIADESCRIPTOR
);
875 // convert media descriptor and URL to right format for later interface call!
876 css::uno::Sequence
< css::beans::PropertyValue
> lDescriptor
;
877 m_lMediaDescriptor
>> lDescriptor
;
878 css::util::URL aURL
= m_aURL
;
880 // get necessary container to query for a handler object
881 css::uno::Reference
< css::frame::XLoaderFactory
> xLoaderFactory
= css::frame::ContentHandlerFactory::create(m_xContext
);
884 // <- SAFE -----------------------------------
887 css::uno::Sequence
< OUString
> lTypeReg
{ sType
};
889 css::uno::Sequence
< css::beans::NamedValue
> lQuery
{ { PROP_TYPES
, css::uno::Any(lTypeReg
) } };
891 css::uno::Reference
< css::container::XEnumeration
> xSet
= xLoaderFactory
->createSubSetEnumerationByProperties(lQuery
);
892 while(xSet
->hasMoreElements())
894 ::comphelper::SequenceAsHashMap
lProps (xSet
->nextElement());
895 OUString sHandler
= lProps
.getUnpackedValueOrDefault(PROP_NAME
, OUString());
897 css::uno::Reference
< css::frame::XNotifyingDispatch
> xHandler
;
900 xHandler
.set(xLoaderFactory
->createInstance(sHandler
), css::uno::UNO_QUERY
);
904 catch(const css::uno::RuntimeException
&)
906 catch(const css::uno::Exception
&)
909 // SAFE -> -----------------------------------
910 osl::ClearableMutexGuard
aWriteLock(m_mutex
);
911 m_xAsynchronousJob
= xHandler
;
912 rtl::Reference
<LoadEnvListener
> xListener
= new LoadEnvListener(this);
914 // <- SAFE -----------------------------------
916 xHandler
->dispatchWithNotification(aURL
, lDescriptor
, xListener
);
924 bool LoadEnv::impl_furtherDocsAllowed()
927 osl::ResettableMutexGuard
aReadLock(m_mutex
);
928 css::uno::Reference
< css::uno::XComponentContext
> xContext
= m_xContext
;
932 bool bAllowed
= true;
936 std::optional
<sal_Int32
> x(officecfg::Office::Common::Misc::MaxOpenDocuments::get());
938 // NIL means: count of allowed documents = infinite !
944 sal_Int32
nMaxOpenDocuments(*x
);
946 css::uno::Reference
< css::frame::XFramesSupplier
> xDesktop(
947 css::frame::Desktop::create(xContext
),
948 css::uno::UNO_QUERY_THROW
);
950 FrameListAnalyzer
aAnalyzer(xDesktop
,
951 css::uno::Reference
< css::frame::XFrame
>(),
952 FrameAnalyzerFlags::Help
|
953 FrameAnalyzerFlags::BackingComponent
|
954 FrameAnalyzerFlags::Hidden
);
956 sal_Int32 nOpenDocuments
= aAnalyzer
.m_lOtherVisibleFrames
.size();
957 bAllowed
= (nOpenDocuments
< nMaxOpenDocuments
);
960 catch(const css::uno::Exception
&)
961 { bAllowed
= true; } // !! internal errors are no reason to disturb the office from opening documents .-)
967 css::uno::Reference
< css::task::XInteractionHandler
> xInteraction
= m_lMediaDescriptor
.getUnpackedValueOrDefault(
968 utl::MediaDescriptor::PROP_INTERACTIONHANDLER
,
969 css::uno::Reference
< css::task::XInteractionHandler
>());
973 if (xInteraction
.is())
975 css::uno::Any aInteraction
;
977 rtl::Reference
<comphelper::OInteractionAbort
> pAbort
= new comphelper::OInteractionAbort();
978 rtl::Reference
<comphelper::OInteractionApprove
> pApprove
= new comphelper::OInteractionApprove();
980 css::uno::Sequence
< css::uno::Reference
< css::task::XInteractionContinuation
> > lContinuations
{
984 css::task::ErrorCodeRequest aErrorCode
;
985 aErrorCode
.ErrCode
= sal_uInt32(ERRCODE_SFX_NOMOREDOCUMENTSALLOWED
);
986 aInteraction
<<= aErrorCode
;
987 xInteraction
->handle( InteractionRequest::CreateRequest(aInteraction
, lContinuations
) );
994 bool LoadEnv::impl_filterHasInteractiveDialog() const
996 //show the frame now so it can be the parent for any message dialogs shown during import
998 //unless (tdf#114648) an Interactive case such as the new database wizard
999 if (m_aURL
.Arguments
== "Interactive")
1002 // unless (tdf#116277) it's the labels/business cards slave frame
1003 if (m_aURL
.Arguments
.indexOf("slot=") != -1)
1006 OUString sFilter
= m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_FILTERNAME
, OUString());
1007 if (sFilter
.isEmpty())
1010 // unless (tdf#115683) the filter has a UIComponent
1011 OUString sUIComponent
;
1012 css::uno::Reference
<css::container::XNameAccess
> xFilterCont(m_xContext
->getServiceManager()->createInstanceWithContext(SERVICENAME_FILTERFACTORY
, m_xContext
),
1013 css::uno::UNO_QUERY_THROW
);
1016 ::comphelper::SequenceAsHashMap
lFilterProps(xFilterCont
->getByName(sFilter
));
1017 sUIComponent
= lFilterProps
.getUnpackedValueOrDefault(u
"UIComponent"_ustr
, OUString());
1019 catch(const css::container::NoSuchElementException
&)
1023 return !sUIComponent
.isEmpty();
1026 bool LoadEnv::impl_loadContent()
1028 // SAFE -> -----------------------------------
1029 osl::ClearableMutexGuard
aWriteLock(m_mutex
);
1031 // search or create right target frame
1032 OUString sTarget
= m_sTarget
;
1033 if (TargetHelper::matchSpecialTarget(sTarget
, TargetHelper::ESpecialTarget::Default
))
1035 m_xTargetFrame
= impl_searchAlreadyLoaded();
1036 if (m_xTargetFrame
.is())
1038 impl_setResult(true);
1041 m_xTargetFrame
= impl_searchRecycleTarget();
1044 if (! m_xTargetFrame
.is())
1047 (TargetHelper::matchSpecialTarget(sTarget
, TargetHelper::ESpecialTarget::Blank
)) ||
1048 (TargetHelper::matchSpecialTarget(sTarget
, TargetHelper::ESpecialTarget::Default
))
1051 if (! impl_furtherDocsAllowed())
1053 TaskCreator
aCreator(m_xContext
);
1054 m_xTargetFrame
= aCreator
.createTask(SPECIALTARGET_BLANK
, m_lMediaDescriptor
);
1055 m_bCloseFrameOnError
= m_xTargetFrame
.is();
1059 sal_Int32 nSearchFlags
= m_nSearchFlags
& ~css::frame::FrameSearchFlag::CREATE
;
1060 m_xTargetFrame
= m_xBaseFrame
->findFrame(sTarget
, nSearchFlags
);
1061 if (! m_xTargetFrame
.is())
1063 if (! impl_furtherDocsAllowed())
1065 m_xTargetFrame
= m_xBaseFrame
->findFrame(SPECIALTARGET_BLANK
, 0);
1066 m_bCloseFrameOnError
= m_xTargetFrame
.is();
1071 // If we couldn't find a valid frame or the frame has no container window
1072 // we have to throw an exception.
1074 ( ! m_xTargetFrame
.is() ) ||
1075 ( ! m_xTargetFrame
->getContainerWindow().is() )
1077 throw LoadEnvException(LoadEnvException::ID_NO_TARGET_FOUND
);
1079 css::uno::Reference
< css::frame::XFrame
> xTargetFrame
= m_xTargetFrame
;
1081 // Now we have a valid frame ... and type detection was already done.
1082 // We should apply the module dependent window position and size to the
1084 impl_applyPersistentWindowState(xTargetFrame
->getContainerWindow());
1086 // Don't forget to lock task for following load process. Otherwise it could die
1087 // during this operation runs by terminating the office or closing this task via api.
1088 // If we set this lock "close()" will return false and closing will be broken.
1089 // Attention: Don't forget to reset this lock again after finishing operation.
1090 // Otherwise task AND office couldn't die!!!
1091 // This includes gracefully handling of Exceptions (Runtime!) too ...
1092 // That's why we use a specialized guard, which will reset the lock
1093 // if it will be run out of scope.
1095 // Note further: ignore if this internal guard already contains a resource.
1096 // Might impl_searchRecycleTarget() set it before. But in case this impl-method wasn't used
1097 // and the target frame was new created ... this lock here must be set!
1098 css::uno::Reference
< css::document::XActionLockable
> xTargetLock(xTargetFrame
, css::uno::UNO_QUERY
);
1099 m_aTargetLock
.setResource(xTargetLock
);
1101 // Add status indicator to descriptor. Loader can show a progress then.
1102 // But don't do it, if loading should be hidden or preview is used...!
1103 // So we prevent our code against wrong using. Why?
1104 // It could be, that using of this progress could make trouble. e.g. He makes window visible...
1105 // but shouldn't do that. But if no indicator is available... nobody has a chance to do that!
1106 bool bHidden
= m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN
, false);
1107 bool bMinimized
= m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_MINIMIZED
, false);
1108 bool bPreview
= m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_PREVIEW
, false);
1109 bool bStartPres
= m_lMediaDescriptor
.contains("StartPresentation");
1111 if (!bHidden
&& !bMinimized
&& !bPreview
&& !bStartPres
)
1113 css::uno::Reference
<css::task::XStatusIndicator
> xProgress
= m_lMediaDescriptor
.getUnpackedValueOrDefault(
1114 utl::MediaDescriptor::PROP_STATUSINDICATOR
, css::uno::Reference
<css::task::XStatusIndicator
>());
1115 if (!xProgress
.is())
1117 // Note: it's an optional interface!
1118 css::uno::Reference
< css::task::XStatusIndicatorFactory
> xProgressFactory(xTargetFrame
, css::uno::UNO_QUERY
);
1119 if (xProgressFactory
.is())
1121 xProgress
= xProgressFactory
->createStatusIndicator();
1123 m_lMediaDescriptor
[utl::MediaDescriptor::PROP_STATUSINDICATOR
] <<= xProgress
;
1127 // Now that we have a target window into which we can load, reinit the interaction handler to have this
1128 // window as its parent for modal dialogs and ensure the window is visible
1129 css::uno::Reference
< css::task::XInteractionHandler
> xInteraction
= m_lMediaDescriptor
.getUnpackedValueOrDefault(
1130 utl::MediaDescriptor::PROP_INTERACTIONHANDLER
,
1131 css::uno::Reference
< css::task::XInteractionHandler
>());
1132 css::uno::Reference
<css::lang::XInitialization
> xHandler(xInteraction
, css::uno::UNO_QUERY
);
1135 css::uno::Reference
<css::awt::XWindow
> xWindow
= xTargetFrame
->getContainerWindow();
1136 uno::Sequence
<uno::Any
> aArguments(comphelper::InitAnyPropertySequence(
1138 {"Parent", uno::Any(xWindow
)}
1140 xHandler
->initialize(aArguments
);
1141 //show the frame as early as possible to make it the parent of any message dialogs
1142 if (!impl_filterHasInteractiveDialog())
1144 impl_makeFrameWindowVisible(xWindow
, shouldFocusAndToFront());
1145 m_bFocusedAndToFront
= true; // no need to ask shouldFocusAndToFront second time
1150 // convert media descriptor and URL to right format for later interface call!
1151 css::uno::Sequence
< css::beans::PropertyValue
> lDescriptor
;
1152 m_lMediaDescriptor
>> lDescriptor
;
1153 OUString sURL
= m_aURL
.Complete
;
1155 // try to locate any interested frame loader
1156 css::uno::Reference
< css::uno::XInterface
> xLoader
= impl_searchLoader();
1157 css::uno::Reference
< css::frame::XFrameLoader
> xAsyncLoader(xLoader
, css::uno::UNO_QUERY
);
1158 css::uno::Reference
< css::frame::XSynchronousFrameLoader
> xSyncLoader (xLoader
, css::uno::UNO_QUERY
);
1160 if (xAsyncLoader
.is())
1162 m_xAsynchronousJob
= xAsyncLoader
;
1163 rtl::Reference
<LoadEnvListener
> xListener
= new LoadEnvListener(this);
1165 // <- SAFE -----------------------------------
1167 xAsyncLoader
->load(xTargetFrame
, sURL
, lDescriptor
, xListener
);
1171 else if (xSyncLoader
.is())
1173 uno::Reference
<beans::XPropertySet
> xTargetFrameProps(xTargetFrame
, uno::UNO_QUERY
);
1174 if (xTargetFrameProps
.is())
1176 // Set the URL on the frame itself, for the duration of the load, when it has no
1178 xTargetFrameProps
->setPropertyValue(u
"URL"_ustr
, uno::Any(sURL
));
1180 bool bResult
= xSyncLoader
->load(lDescriptor
, xTargetFrame
);
1181 // react for the result here, so the outside waiting
1182 // code can ask for it later.
1183 impl_setResult(bResult
);
1184 // But the return value indicates a valid started(!) operation.
1185 // And that's true every time we reach this line :-)
1195 css::uno::Reference
< css::uno::XInterface
> LoadEnv::impl_searchLoader()
1197 // SAFE -> -----------------------------------
1198 osl::ClearableMutexGuard
aReadLock(m_mutex
);
1200 // special mode to set an existing component on this frame
1201 // In such case the loader is fix. It must be the SFX based implementation,
1202 // which can create a view on top of such xModel components :-)
1203 if (m_eContentType
== E_CAN_BE_SET
)
1207 return css::frame::OfficeFrameLoader::create(m_xContext
);
1209 catch(const css::uno::RuntimeException
&)
1211 catch(const css::uno::Exception
&)
1213 throw LoadEnvException(LoadEnvException::ID_INVALID_ENVIRONMENT
);
1217 // We need this type information to locate a registered frame loader
1218 // Without such information we can't work!
1219 OUString sType
= m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_TYPENAME
, OUString());
1220 if (sType
.isEmpty())
1221 throw LoadEnvException(LoadEnvException::ID_INVALID_MEDIADESCRIPTOR
);
1223 // try to locate any interested frame loader
1224 css::uno::Reference
< css::frame::XLoaderFactory
> xLoaderFactory
= css::frame::FrameLoaderFactory::create(m_xContext
);
1227 // <- SAFE -----------------------------------
1229 css::uno::Sequence
< OUString
> lTypesReg
{ sType
};
1231 css::uno::Sequence
< css::beans::NamedValue
> lQuery
{ { PROP_TYPES
, css::uno::Any(lTypesReg
) } };
1233 css::uno::Reference
< css::container::XEnumeration
> xSet
= xLoaderFactory
->createSubSetEnumerationByProperties(lQuery
);
1234 while(xSet
->hasMoreElements())
1239 // Ignore any loader, which makes trouble :-)
1240 ::comphelper::SequenceAsHashMap
lLoaderProps(xSet
->nextElement());
1241 OUString sLoader
= lLoaderProps
.getUnpackedValueOrDefault(PROP_NAME
, OUString());
1242 css::uno::Reference
< css::uno::XInterface
> xLoader
= xLoaderFactory
->createInstance(sLoader
);
1246 catch(const css::uno::RuntimeException
&)
1248 catch(const css::uno::Exception
&)
1252 return css::uno::Reference
< css::uno::XInterface
>();
1255 void LoadEnv::impl_jumpToMark(const css::uno::Reference
< css::frame::XFrame
>& xFrame
,
1256 const css::util::URL
& aURL
)
1258 if (aURL
.Mark
.isEmpty())
1261 css::uno::Reference
< css::frame::XDispatchProvider
> xProvider(xFrame
, css::uno::UNO_QUERY
);
1262 if (! xProvider
.is())
1266 osl::ClearableMutexGuard
aReadLock(m_mutex
);
1267 css::uno::Reference
< css::uno::XComponentContext
> xContext
= m_xContext
;
1271 css::util::URL aCmd
;
1272 aCmd
.Complete
= ".uno:JumpToMark";
1274 css::uno::Reference
< css::util::XURLTransformer
> xParser(css::util::URLTransformer::create(xContext
));
1275 xParser
->parseStrict(aCmd
);
1277 css::uno::Reference
< css::frame::XDispatch
> xDispatcher
= xProvider
->queryDispatch(aCmd
, SPECIALTARGET_SELF
, 0);
1278 if (! xDispatcher
.is())
1281 ::comphelper::SequenceAsHashMap lArgs
;
1282 lArgs
[u
"Bookmark"_ustr
] <<= aURL
.Mark
;
1283 xDispatcher
->dispatch(aCmd
, lArgs
.getAsConstPropertyValueList());
1286 css::uno::Reference
< css::frame::XFrame
> LoadEnv::impl_searchAlreadyLoaded()
1288 osl::MutexGuard
g(m_mutex
);
1290 // such search is allowed for special requests only ...
1291 // or better it's not allowed for some requests in general :-)
1293 ( ! TargetHelper::matchSpecialTarget(m_sTarget
, TargetHelper::ESpecialTarget::Default
) ) ||
1294 m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_ASTEMPLATE
, false) ||
1295 // (m_lMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN() , false) == sal_True) ||
1296 m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_OPENNEWVIEW
, false)
1299 return css::uno::Reference
< css::frame::XFrame
>();
1303 // May it's not useful to start expensive document search, if it
1304 // can fail only .. because we load from a stream or model directly!
1306 (ProtocolCheck::isProtocol(m_aURL
.Complete
, EProtocol::PrivateStream
)) ||
1307 (ProtocolCheck::isProtocol(m_aURL
.Complete
, EProtocol::PrivateObject
))
1308 /*TODO should be private:factory here tested too? */
1311 return css::uno::Reference
< css::frame::XFrame
>();
1314 // otherwise - iterate through the tasks of the desktop container
1315 // to find out, which of them might contains the requested document
1316 css::uno::Reference
< css::frame::XDesktop2
> xSupplier
= css::frame::Desktop::create( m_xContext
);
1317 css::uno::Reference
< css::container::XIndexAccess
> xTaskList
= xSupplier
->getFrames();
1319 if (!xTaskList
.is())
1320 return css::uno::Reference
< css::frame::XFrame
>(); // task list can be empty!
1322 // Note: To detect if a document was already loaded before
1323 // we check URLs here only. But might the existing and the required
1324 // document has different versions! Then its URLs are the same...
1325 sal_Int16 nNewVersion
= m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_VERSION
, sal_Int16(-1));
1327 // will be used to save the first hidden frame referring the searched model
1328 // Normally we are interested on visible frames... but if there is no such visible
1329 // frame we refer to any hidden frame also (but as fallback only).
1330 css::uno::Reference
< css::frame::XFrame
> xHiddenTask
;
1331 css::uno::Reference
< css::frame::XFrame
> xTask
;
1333 sal_Int32 count
= xTaskList
->getCount();
1334 for (sal_Int32 i
=0; i
<count
; ++i
)
1338 // locate model of task
1339 // Note: Without a model there is no chance to decide if
1340 // this task contains the searched document or not!
1341 xTaskList
->getByIndex(i
) >>= xTask
;
1346 css::uno::Reference
< css::frame::XController
> xController
= xTask
->getController();
1347 if (!xController
.is())
1349 // If we have no controller, then perhaps there is a load in progress. The frame
1350 // itself has the URL in this case.
1351 uno::Reference
<beans::XPropertySet
> xTaskProps(xTask
, uno::UNO_QUERY
);
1352 if (xTaskProps
.is())
1354 xTaskProps
->getPropertyValue(u
"URL"_ustr
) >>= sURL
;
1363 uno::Reference
<frame::XModel
> xModel
;
1366 xModel
= xController
->getModel();
1373 // don't check the complete URL here.
1374 // use its main part - ignore optional jumpmarks!
1375 sURL
= xModel
->getURL();
1377 if (!::utl::UCBContentHelper::EqualURLs( m_aURL
.Main
, sURL
))
1383 // get the original load arguments from the current document
1384 // and decide if it's really the same then the one will be.
1385 // It must be visible and must use the same file revision ...
1386 // or must not have any file revision set (-1 == -1!)
1387 utl::MediaDescriptor lOldDocDescriptor
;
1390 lOldDocDescriptor
= xModel
->getArgs();
1392 if (lOldDocDescriptor
.getUnpackedValueOrDefault(
1393 utl::MediaDescriptor::PROP_VERSION
, sal_Int32(-1))
1401 // Hidden frames are special.
1402 // They will be used as "last chance" if there is no visible frame pointing to the same model.
1403 // Safe the result but continue with current loop might be looking for other visible frames.
1404 bool bIsHidden
= lOldDocDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN
, false);
1405 if ( bIsHidden
&& ! xHiddenTask
.is() )
1407 xHiddenTask
= xTask
;
1412 // We found a visible task pointing to the right model ...
1416 catch(const css::uno::RuntimeException
&)
1418 catch(const css::uno::Exception
&)
1422 css::uno::Reference
< css::frame::XFrame
> xResult
;
1424 xResult
= std::move(xTask
);
1425 else if (xHiddenTask
.is())
1426 xResult
= std::move(xHiddenTask
);
1430 // Now we are sure, that this task includes the searched document.
1431 // It's time to activate it. As special feature we try to jump internally
1432 // if an optional jumpmark is given too.
1433 if (!m_aURL
.Mark
.isEmpty())
1434 impl_jumpToMark(xResult
, m_aURL
);
1441 bool LoadEnv::impl_isFrameAlreadyUsedForLoading(const css::uno::Reference
< css::frame::XFrame
>& xFrame
)
1443 css::uno::Reference
< css::document::XActionLockable
> xLock(xFrame
, css::uno::UNO_QUERY
);
1445 // ? no lock interface ?
1446 // Maybe it's an external written frame implementation :-(
1447 // Allowing using of it... but it can fail if it's not synchronized with our processes!
1451 // Otherwise we have to look for any other existing lock.
1452 return xLock
->isActionLocked();
1455 css::uno::Reference
< css::frame::XFrame
> LoadEnv::impl_searchRecycleTarget()
1457 // SAFE -> ..................................
1458 osl::ClearableMutexGuard
aReadLock(m_mutex
);
1460 // The special backing mode frame will be recycled by definition!
1461 // It doesn't matter if somewhere wants to create a new view
1462 // or open a new untitled document...
1463 // The only exception from that - hidden frames!
1464 if (m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN
, false))
1465 return css::uno::Reference
< css::frame::XFrame
>();
1467 css::uno::Reference
< css::frame::XFramesSupplier
> xSupplier
= css::frame::Desktop::create( m_xContext
);
1468 FrameListAnalyzer
aTasksAnalyzer(xSupplier
, css::uno::Reference
< css::frame::XFrame
>(), FrameAnalyzerFlags::BackingComponent
);
1469 if (aTasksAnalyzer
.m_xBackingComponent
.is())
1471 if (!impl_isFrameAlreadyUsedForLoading(aTasksAnalyzer
.m_xBackingComponent
))
1473 m_bReactivateControllerOnError
= true;
1474 return aTasksAnalyzer
.m_xBackingComponent
;
1478 // These states indicates a wish for creation of a new view in general.
1480 m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_ASTEMPLATE
, false) ||
1481 m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_OPENNEWVIEW
, false)
1484 return css::uno::Reference
< css::frame::XFrame
>();
1487 // On the other side some special URLs will open a new frame every time (expecting
1488 // they can use the backing-mode frame!)
1490 (ProtocolCheck::isProtocol(m_aURL
.Complete
, EProtocol::PrivateFactory
)) ||
1491 (ProtocolCheck::isProtocol(m_aURL
.Complete
, EProtocol::PrivateStream
)) ||
1492 (ProtocolCheck::isProtocol(m_aURL
.Complete
, EProtocol::PrivateObject
))
1495 return css::uno::Reference
< css::frame::XFrame
>();
1498 // No backing frame! No special URL => recycle active task - if possible.
1499 // Means - if it does not already contains a modified document, or
1500 // use another office module.
1501 css::uno::Reference
< css::frame::XFrame
> xTask
= xSupplier
->getActiveFrame();
1503 // not a real error - but might a focus problem!
1505 return css::uno::Reference
< css::frame::XFrame
>();
1507 // not a real error - may it's a view only
1508 css::uno::Reference
< css::frame::XController
> xController
= xTask
->getController();
1509 if (!xController
.is())
1510 return css::uno::Reference
< css::frame::XFrame
>();
1512 // not a real error - may it's a db component instead of a full featured office document
1513 css::uno::Reference
< css::frame::XModel
> xModel
= xController
->getModel();
1515 return css::uno::Reference
< css::frame::XFrame
>();
1517 // get some more information ...
1519 // A valid set URL means: there is already a location for this document.
1520 // => it was saved there or opened from there. Such Documents can not be used here.
1521 // We search for empty document ... created by a private:factory/ URL!
1522 if (xModel
->getURL().getLength()>0)
1523 return css::uno::Reference
< css::frame::XFrame
>();
1525 // The old document must be unmodified ...
1526 css::uno::Reference
< css::util::XModifiable
> xModified(xModel
, css::uno::UNO_QUERY
);
1527 if (xModified
->isModified())
1528 return css::uno::Reference
< css::frame::XFrame
>();
1530 VclPtr
<vcl::Window
> pWindow
= VCLUnoHelper::GetWindow(xTask
->getContainerWindow());
1531 if (pWindow
&& pWindow
->IsInModalMode())
1532 return css::uno::Reference
< css::frame::XFrame
>();
1534 // find out the application type of this document
1535 // We can recycle only documents, which uses the same application
1536 // then the new one.
1537 SvtModuleOptions::EFactory eOldApp
= SvtModuleOptions::ClassifyFactoryByModel(xModel
);
1538 SvtModuleOptions::EFactory eNewApp
= SvtModuleOptions::ClassifyFactoryByURL (m_aURL
.Complete
, m_lMediaDescriptor
.getAsConstPropertyValueList());
1541 // <- SAFE ..................................
1543 if (eOldApp
!= eNewApp
)
1544 return css::uno::Reference
< css::frame::XFrame
>();
1546 // OK this task seems to be usable for recycling
1547 // But we should mark it as such - means set an action lock.
1548 // Otherwise it would be used more than ones or will be destroyed
1549 // by a close() or terminate() request.
1550 // But if such lock already exist ... it means this task is used for
1551 // any other operation already. Don't use it then.
1552 if (impl_isFrameAlreadyUsedForLoading(xTask
))
1553 return css::uno::Reference
< css::frame::XFrame
>();
1555 // OK - there is a valid target frame.
1556 // But may be it contains already a document.
1557 // Then we have to ask it, if it allows recycling of this frame .-)
1558 bool bReactivateOldControllerOnError
= false;
1559 css::uno::Reference
< css::frame::XController
> xOldDoc
= xTask
->getController();
1562 utl::MediaDescriptor
lOldDocDescriptor(xModel
->getArgs());
1564 // replaceable document
1565 if (!lOldDocDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_REPLACEABLE
, false))
1566 return css::uno::Reference
< css::frame::XFrame
>();
1568 bReactivateOldControllerOnError
= xOldDoc
->suspend(true);
1569 if (! bReactivateOldControllerOnError
)
1570 return css::uno::Reference
< css::frame::XFrame
>();
1573 // SAFE -> ..................................
1575 osl::MutexGuard
aWriteLock(m_mutex
);
1577 css::uno::Reference
< css::document::XActionLockable
> xLock(xTask
, css::uno::UNO_QUERY
);
1578 if (!m_aTargetLock
.setResource(xLock
))
1579 return css::uno::Reference
< css::frame::XFrame
>();
1581 m_bReactivateControllerOnError
= bReactivateOldControllerOnError
;
1583 // <- SAFE ..................................
1588 void LoadEnv::impl_reactForLoadingState()
1590 /*TODO reset action locks */
1592 // SAFE -> ----------------------------------
1593 osl::ClearableMutexGuard
aReadLock(m_mutex
);
1597 // Bring the new loaded document to front (if allowed!).
1598 // Note: We show new created frames here only.
1599 // We don't hide already visible frames here ...
1600 css::uno::Reference
< css::awt::XWindow
> xWindow
= m_xTargetFrame
->getContainerWindow();
1601 bool bHidden
= m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_HIDDEN
, false);
1602 bool bMinimized
= m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_MINIMIZED
, false);
1603 bool bStartPres
= m_lMediaDescriptor
.contains("StartPresentation");
1605 VclPtr
<vcl::Window
> pWindow
= VCLUnoHelper::GetWindow(xWindow
);
1609 SolarMutexGuard aSolarGuard
;
1610 // check for system window is necessary to guarantee correct pointer cast!
1611 if (pWindow
&& pWindow
->IsSystemWindow())
1612 static_cast<WorkWindow
*>(pWindow
.get())->Minimize();
1614 else if (!bHidden
&& !bStartPres
)
1616 // show frame ... if it's not still visible ...
1617 // But do nothing if it's already visible!
1618 impl_makeFrameWindowVisible(xWindow
, !m_bFocusedAndToFront
&& shouldFocusAndToFront());
1622 pWindow
->FlashWindow();
1624 // Note: Only if an existing property "FrameName" is given by this media descriptor,
1625 // it should be used. Otherwise we should do nothing. May be the outside code has already
1626 // set a frame name on the target!
1627 utl::MediaDescriptor::const_iterator pFrameName
= m_lMediaDescriptor
.find(utl::MediaDescriptor::PROP_FRAMENAME
);
1628 if (pFrameName
!= m_lMediaDescriptor
.end())
1630 OUString sFrameName
;
1631 pFrameName
->second
>>= sFrameName
;
1632 // Check the name again. e.g. "_default" isn't allowed.
1633 // On the other side "_beamer" is a valid name :-)
1634 if (TargetHelper::isValidNameForFrame(sFrameName
))
1635 m_xTargetFrame
->setName(sFrameName
);
1638 else if (m_bReactivateControllerOnError
)
1640 // Try to reactivate the old document (if any exists!)
1641 css::uno::Reference
< css::frame::XController
> xOldDoc
= m_xTargetFrame
->getController();
1642 // clear does not depend from reactivation state of a might existing old document!
1643 // We must make sure, that a might following getTargetComponent() call does not return
1644 // the old document!
1645 m_xTargetFrame
.clear();
1648 bool bReactivated
= xOldDoc
->suspend(false);
1650 throw LoadEnvException(LoadEnvException::ID_COULD_NOT_REACTIVATE_CONTROLLER
);
1651 m_bReactivateControllerOnError
= false;
1654 else if (m_bCloseFrameOnError
)
1656 // close empty frames
1657 css::uno::Reference
< css::util::XCloseable
> xCloseable (m_xTargetFrame
, css::uno::UNO_QUERY
);
1661 if (xCloseable
.is())
1662 xCloseable
->close(true);
1663 else if (m_xTargetFrame
.is())
1664 m_xTargetFrame
->dispose();
1666 catch(const css::util::CloseVetoException
&)
1668 catch(const css::lang::DisposedException
&)
1670 m_xTargetFrame
.clear();
1673 // This max force an implicit closing of our target frame ...
1674 // e.g. in case close(sal_True) was called before and the frame
1675 // kill itself if our external use-lock is released here!
1676 // That's why we release this lock AFTER ALL OPERATIONS on this frame
1677 // are finished. The frame itself must handle then
1678 // this situation gracefully.
1679 m_aTargetLock
.freeResource();
1681 // Last but not least :-)
1682 // We have to clear the current media descriptor.
1683 // Otherwise it hold a might existing stream open!
1684 m_lMediaDescriptor
.clear();
1686 css::uno::Any aRequest
;
1687 bool bThrow
= false;
1688 if ( !m_bLoaded
&& m_pQuietInteraction
.is() && m_pQuietInteraction
->wasUsed() )
1690 aRequest
= m_pQuietInteraction
->getRequest();
1691 m_pQuietInteraction
.clear();
1699 if ( aRequest
.isExtractableTo( ::cppu::UnoType
< css::uno::Exception
>::get() ) )
1700 throw LoadEnvException(
1701 LoadEnvException::ID_GENERAL_ERROR
, u
"interaction request"_ustr
,
1705 // <- SAFE ----------------------------------
1708 bool LoadEnv::shouldFocusAndToFront() const
1711 m_lMediaDescriptor
.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_PREVIEW
, false));
1716 void LoadEnv::impl_makeFrameWindowVisible(const css::uno::Reference
< css::awt::XWindow
>& xWindow
,
1719 SolarMutexGuard aSolarGuard
;
1720 VclPtr
<vcl::Window
> pWindow
= VCLUnoHelper::GetWindow(xWindow
);
1724 if (pWindow
->IsVisible() && bForceToFront
)
1725 pWindow
->ToTop( ToTopFlags::RestoreWhenMin
| ToTopFlags::ForegroundTask
);
1727 pWindow
->Show(true, bForceToFront
? ShowFlags::ForegroundTask
: ShowFlags::NONE
);
1730 void LoadEnv::impl_applyPersistentWindowState(const css::uno::Reference
< css::awt::XWindow
>& xWindow
)
1732 // no window -> action not possible
1736 // window already visible -> do nothing! If we use a "recycle frame" for loading ...
1737 // the current position and size must be used.
1738 css::uno::Reference
< css::awt::XWindow2
> xVisibleCheck(xWindow
, css::uno::UNO_QUERY
);
1740 (xVisibleCheck
.is() ) &&
1741 (xVisibleCheck
->isVisible())
1747 SolarMutexGuard aSolarGuard1
;
1749 VclPtr
<vcl::Window
> pWindow
= VCLUnoHelper::GetWindow(xWindow
);
1753 bool bSystemWindow
= pWindow
->IsSystemWindow();
1754 bool bWorkWindow
= (pWindow
->GetType() == WindowType::WORKWINDOW
);
1756 if (!bSystemWindow
&& !bWorkWindow
)
1759 // don't overwrite this special state!
1760 WorkWindow
* pWorkWindow
= static_cast<WorkWindow
*>(pWindow
.get());
1761 if (pWorkWindow
->IsMinimized())
1767 osl::ClearableMutexGuard
aReadLock(m_mutex
);
1769 // no filter -> no module -> no persistent window state
1770 OUString sFilter
= m_lMediaDescriptor
.getUnpackedValueOrDefault(
1771 utl::MediaDescriptor::PROP_FILTERNAME
,
1773 if (sFilter
.isEmpty())
1776 css::uno::Reference
< css::uno::XComponentContext
> xContext
= m_xContext
;
1783 // retrieve the module name from the filter configuration
1784 css::uno::Reference
< css::container::XNameAccess
> xFilterCfg(
1785 xContext
->getServiceManager()->createInstanceWithContext(SERVICENAME_FILTERFACTORY
, xContext
),
1786 css::uno::UNO_QUERY_THROW
);
1787 ::comphelper::SequenceAsHashMap
lProps (xFilterCfg
->getByName(sFilter
));
1788 OUString sModule
= lProps
.getUnpackedValueOrDefault(FILTER_PROPNAME_ASCII_DOCUMENTSERVICE
, OUString());
1790 // get access to the configuration of this office module
1791 css::uno::Reference
< css::container::XNameAccess
> xModuleCfg(officecfg::Setup::Office::Factories::get());
1793 // read window state from the configuration
1794 // and apply it on the window.
1795 // Do nothing, if no configuration entry exists!
1796 OUString sWindowState
;
1798 // Don't look for persistent window attributes when used through LibreOfficeKit
1799 if( !comphelper::LibreOfficeKit::isActive() )
1800 comphelper::ConfigurationHelper::readRelativeKey(xModuleCfg
, sModule
, u
"ooSetupFactoryWindowAttributes"_ustr
) >>= sWindowState
;
1802 if (!sWindowState
.isEmpty())
1805 SolarMutexGuard aSolarGuard
;
1807 // We have to retrieve the window pointer again. Because nobody can guarantee
1808 // that the XWindow was not disposed in between .-)
1809 // But if we get a valid pointer we can be sure, that it's the system window pointer
1810 // we already checked and used before. Because nobody recycle the same uno reference for
1811 // a new internal c++ implementation ... hopefully .-))
1812 VclPtr
<vcl::Window
> pWindowCheck
= VCLUnoHelper::GetWindow(xWindow
);
1816 SystemWindow
* pSystemWindow
= static_cast<SystemWindow
*>(pWindowCheck
.get());
1817 pSystemWindow
->SetWindowState(sWindowState
);
1821 catch(const css::uno::RuntimeException
&)
1823 catch(const css::uno::Exception
&)
1827 } // namespace framework
1829 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */