/src/libreoffice/sfx2/source/view/frmload.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
11 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | | * contributor license agreements. See the NOTICE file distributed |
13 | | * with this work for additional information regarding copyright |
14 | | * ownership. The ASF licenses this file to you under the Apache |
15 | | * License, Version 2.0 (the "License"); you may not use this file |
16 | | * except in compliance with the License. You may obtain a copy of |
17 | | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | | */ |
19 | | |
20 | | #include <config_collab.h> |
21 | | |
22 | | #include <sfx2/app.hxx> |
23 | | #include <sfx2/bindings.hxx> |
24 | | #include <sfx2/docfac.hxx> |
25 | | #include <sfx2/docfile.hxx> |
26 | | #include <sfx2/docfilt.hxx> |
27 | | #include <sfx2/doctempl.hxx> |
28 | | #include <sfx2/fcontnr.hxx> |
29 | | #include <sfx2/frame.hxx> |
30 | | #include <sfx2/objsh.hxx> |
31 | | #include <sfx2/request.hxx> |
32 | | #include <sfx2/sfxsids.hrc> |
33 | | #include <sfx2/viewfac.hxx> |
34 | | |
35 | | #include <com/sun/star/container/XContainerQuery.hpp> |
36 | | #include <com/sun/star/document/XTypeDetection.hpp> |
37 | | #include <com/sun/star/frame/XFrame.hpp> |
38 | | #include <com/sun/star/frame/XLoadable.hpp> |
39 | | #include <com/sun/star/frame/XModel3.hpp> |
40 | | #include <com/sun/star/task/XInteractionHandler.hpp> |
41 | | #include <com/sun/star/task/XInteractionHandler2.hpp> |
42 | | #include <com/sun/star/document/XViewDataSupplier.hpp> |
43 | | #include <com/sun/star/container/XIndexAccess.hpp> |
44 | | #include <com/sun/star/frame/XSynchronousFrameLoader.hpp> |
45 | | #include <com/sun/star/frame/XController2.hpp> |
46 | | #include <com/sun/star/frame/XModel2.hpp> |
47 | | #include <com/sun/star/lang/XServiceInfo.hpp> |
48 | | #include <com/sun/star/lang/XInitialization.hpp> |
49 | | #include <com/sun/star/uno/XComponentContext.hpp> |
50 | | #include <com/sun/star/util/XCloseable.hpp> |
51 | | #if ENABLE_YRS |
52 | | #include <com/sun/star/io/SequenceInputStream.hpp> |
53 | | #include <com/sun/star/connection/Connector.hpp> |
54 | | #endif |
55 | | |
56 | | #include <comphelper/getexpandeduri.hxx> |
57 | | #include <comphelper/interaction.hxx> |
58 | | #include <comphelper/namedvaluecollection.hxx> |
59 | | #include <cppuhelper/exc_hlp.hxx> |
60 | | #include <cppuhelper/implbase.hxx> |
61 | | #include <cppuhelper/supportsservice.hxx> |
62 | | #include <framework/interaction.hxx> |
63 | | #include <officecfg/Office/Common.hxx> |
64 | | #include <rtl/ref.hxx> |
65 | | #include <sal/log.hxx> |
66 | | #include <svl/eitem.hxx> |
67 | | #include <svl/stritem.hxx> |
68 | | #include <unotools/fcm.hxx> |
69 | | #include <unotools/moduleoptions.hxx> |
70 | | #include <comphelper/diagnose_ex.hxx> |
71 | | #include <tools/stream.hxx> |
72 | | #include <tools/urlobj.hxx> |
73 | | #include <vcl/svapp.hxx> |
74 | | #include <o3tl/string_view.hxx> |
75 | | |
76 | | using namespace com::sun::star; |
77 | | using ::com::sun::star::beans::PropertyValue; |
78 | | using ::com::sun::star::container::XContainerQuery; |
79 | | using ::com::sun::star::container::XEnumeration; |
80 | | using ::com::sun::star::document::XTypeDetection; |
81 | | using ::com::sun::star::frame::XFrame; |
82 | | using ::com::sun::star::frame::XLoadable; |
83 | | using ::com::sun::star::task::XInteractionHandler; |
84 | | using ::com::sun::star::task::XInteractionHandler2; |
85 | | using ::com::sun::star::uno::Any; |
86 | | using ::com::sun::star::uno::Exception; |
87 | | using ::com::sun::star::uno::Reference; |
88 | | using ::com::sun::star::uno::RuntimeException; |
89 | | using ::com::sun::star::uno::Sequence; |
90 | | using ::com::sun::star::uno::UNO_QUERY; |
91 | | using ::com::sun::star::uno::UNO_QUERY_THROW; |
92 | | using ::com::sun::star::uno::UNO_SET_THROW; |
93 | | using ::com::sun::star::util::XCloseable; |
94 | | using ::com::sun::star::document::XViewDataSupplier; |
95 | | using ::com::sun::star::container::XIndexAccess; |
96 | | using ::com::sun::star::frame::XController2; |
97 | | using ::com::sun::star::frame::XModel2; |
98 | | |
99 | | namespace { |
100 | | |
101 | | class SfxFrameLoader_Impl : public ::cppu::WeakImplHelper< css::frame::XSynchronousFrameLoader, css::lang::XServiceInfo > |
102 | | { |
103 | | css::uno::Reference < css::uno::XComponentContext > m_aContext; |
104 | | |
105 | | public: |
106 | | explicit SfxFrameLoader_Impl( const css::uno::Reference < css::uno::XComponentContext >& _rxContext ); |
107 | | |
108 | | virtual OUString SAL_CALL getImplementationName() override; |
109 | | |
110 | | virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; |
111 | | |
112 | | virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; |
113 | | |
114 | | |
115 | | // XSynchronousFrameLoader |
116 | | |
117 | | virtual sal_Bool SAL_CALL load( const css::uno::Sequence< css::beans::PropertyValue >& _rArgs, const css::uno::Reference< css::frame::XFrame >& _rxFrame ) override; |
118 | | virtual void SAL_CALL cancel() override; |
119 | | |
120 | | protected: |
121 | | virtual ~SfxFrameLoader_Impl() override; |
122 | | |
123 | | private: |
124 | | std::shared_ptr<const SfxFilter> impl_getFilterFromServiceName_nothrow( |
125 | | const OUString& i_rServiceName |
126 | | ) const; |
127 | | |
128 | | static OUString impl_askForFilter_nothrow( |
129 | | const css::uno::Reference< css::task::XInteractionHandler >& i_rxHandler, |
130 | | const OUString& i_rDocumentURL |
131 | | ); |
132 | | |
133 | | std::shared_ptr<const SfxFilter> impl_detectFilterForURL( |
134 | | const OUString& _rURL, |
135 | | const ::comphelper::NamedValueCollection& i_rDescriptor, |
136 | | const SfxFilterMatcher& rMatcher |
137 | | ) const; |
138 | | |
139 | | static bool impl_createNewDocWithSlotParam( |
140 | | const sal_uInt16 _nSlotID, |
141 | | const css::uno::Reference< css::frame::XFrame >& i_rxFrame, |
142 | | const bool i_bHidden |
143 | | ); |
144 | | |
145 | | void impl_determineFilter( |
146 | | ::comphelper::NamedValueCollection& io_rDescriptor |
147 | | ) const; |
148 | | |
149 | | bool impl_determineTemplateDocument( |
150 | | ::comphelper::NamedValueCollection& io_rDescriptor |
151 | | ) const; |
152 | | |
153 | | static sal_uInt16 impl_findSlotParam( |
154 | | std::u16string_view i_rFactoryURL |
155 | | ); |
156 | | |
157 | | static SfxObjectShellRef impl_findObjectShell( |
158 | | const css::uno::Reference< css::frame::XModel2 >& i_rxDocument |
159 | | ); |
160 | | |
161 | | static void impl_handleCaughtError_nothrow( |
162 | | const css::uno::Any& i_rCaughtError, |
163 | | const ::comphelper::NamedValueCollection& i_rDescriptor |
164 | | ); |
165 | | |
166 | | static void impl_removeLoaderArguments( |
167 | | ::comphelper::NamedValueCollection& io_rDescriptor |
168 | | ); |
169 | | |
170 | | static SfxInterfaceId impl_determineEffectiveViewId_nothrow( |
171 | | const SfxObjectShell& i_rDocument, |
172 | | const ::comphelper::NamedValueCollection& i_rDescriptor |
173 | | ); |
174 | | |
175 | | static ::comphelper::NamedValueCollection |
176 | | impl_extractViewCreationArgs( |
177 | | ::comphelper::NamedValueCollection& io_rDescriptor |
178 | | ); |
179 | | |
180 | | static css::uno::Reference< css::frame::XController2 > |
181 | | impl_createDocumentView( |
182 | | const css::uno::Reference< css::frame::XModel2 >& i_rModel, |
183 | | const css::uno::Reference< css::frame::XFrame >& i_rFrame, |
184 | | const ::comphelper::NamedValueCollection& i_rViewFactoryArgs, |
185 | | const OUString& i_rViewName |
186 | | ); |
187 | | }; |
188 | | |
189 | | SfxFrameLoader_Impl::SfxFrameLoader_Impl( const Reference< css::uno::XComponentContext >& _rxContext ) |
190 | 0 | :m_aContext( _rxContext ) |
191 | 0 | { |
192 | 0 | } |
193 | | |
194 | | SfxFrameLoader_Impl::~SfxFrameLoader_Impl() |
195 | 0 | { |
196 | 0 | } |
197 | | |
198 | | |
199 | | std::shared_ptr<const SfxFilter> SfxFrameLoader_Impl::impl_detectFilterForURL( const OUString& sURL, |
200 | | const ::comphelper::NamedValueCollection& i_rDescriptor, const SfxFilterMatcher& rMatcher ) const |
201 | 0 | { |
202 | 0 | OUString sFilter; |
203 | 0 | try |
204 | 0 | { |
205 | 0 | if ( sURL.isEmpty() ) |
206 | 0 | return nullptr; |
207 | | |
208 | 0 | Reference< XTypeDetection > xDetect( |
209 | 0 | m_aContext->getServiceManager()->createInstanceWithContext(u"com.sun.star.document.TypeDetection"_ustr, m_aContext), |
210 | 0 | UNO_QUERY_THROW); |
211 | |
|
212 | 0 | ::comphelper::NamedValueCollection aNewArgs; |
213 | 0 | aNewArgs.put( u"URL"_ustr, sURL ); |
214 | |
|
215 | 0 | if ( i_rDescriptor.has( u"InteractionHandler"_ustr ) ) |
216 | 0 | aNewArgs.put( u"InteractionHandler"_ustr, i_rDescriptor.get( u"InteractionHandler"_ustr ) ); |
217 | 0 | if ( i_rDescriptor.has( u"StatusIndicator"_ustr ) ) |
218 | 0 | aNewArgs.put( u"StatusIndicator"_ustr, i_rDescriptor.get( u"StatusIndicator"_ustr ) ); |
219 | |
|
220 | 0 | Sequence< PropertyValue > aQueryArgs( aNewArgs.getPropertyValues() ); |
221 | 0 | OUString sType = xDetect->queryTypeByDescriptor( aQueryArgs, true ); |
222 | 0 | if ( !sType.isEmpty() ) |
223 | 0 | { |
224 | 0 | std::shared_ptr<const SfxFilter> pFilter = rMatcher.GetFilter4EA( sType ); |
225 | 0 | if ( pFilter ) |
226 | 0 | sFilter = pFilter->GetName(); |
227 | 0 | } |
228 | 0 | } |
229 | 0 | catch ( const RuntimeException& ) |
230 | 0 | { |
231 | 0 | throw; |
232 | 0 | } |
233 | 0 | catch( const Exception& ) |
234 | 0 | { |
235 | 0 | DBG_UNHANDLED_EXCEPTION("sfx.view"); |
236 | 0 | sFilter.clear(); |
237 | 0 | } |
238 | | |
239 | 0 | std::shared_ptr<const SfxFilter> pFilter; |
240 | 0 | if (!sFilter.isEmpty()) |
241 | 0 | pFilter = rMatcher.GetFilter4FilterName(sFilter); |
242 | 0 | return pFilter; |
243 | 0 | } |
244 | | |
245 | | |
246 | | std::shared_ptr<const SfxFilter> SfxFrameLoader_Impl::impl_getFilterFromServiceName_nothrow( const OUString& i_rServiceName ) const |
247 | 0 | { |
248 | 0 | try |
249 | 0 | { |
250 | 0 | ::comphelper::NamedValueCollection aQuery; |
251 | 0 | aQuery.put( u"DocumentService"_ustr, i_rServiceName ); |
252 | |
|
253 | 0 | const Reference< XContainerQuery > xQuery( |
254 | 0 | m_aContext->getServiceManager()->createInstanceWithContext(u"com.sun.star.document.FilterFactory"_ustr, m_aContext), |
255 | 0 | UNO_QUERY_THROW ); |
256 | |
|
257 | 0 | const SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher(); |
258 | 0 | const SfxFilterFlags nMust = SfxFilterFlags::IMPORT; |
259 | 0 | const SfxFilterFlags nDont = SFX_FILTER_NOTINSTALLED; |
260 | |
|
261 | 0 | Reference < XEnumeration > xEnum( xQuery->createSubSetEnumerationByProperties( |
262 | 0 | aQuery.getNamedValues() ), UNO_SET_THROW ); |
263 | 0 | while ( xEnum->hasMoreElements() ) |
264 | 0 | { |
265 | 0 | ::comphelper::NamedValueCollection aType( xEnum->nextElement() ); |
266 | 0 | OUString sFilterName = aType.getOrDefault( u"Name"_ustr, OUString() ); |
267 | 0 | if ( sFilterName.isEmpty() ) |
268 | 0 | continue; |
269 | | |
270 | 0 | std::shared_ptr<const SfxFilter> pFilter = rMatcher.GetFilter4FilterName( sFilterName ); |
271 | 0 | if ( !pFilter ) |
272 | 0 | continue; |
273 | | |
274 | 0 | SfxFilterFlags nFlags = pFilter->GetFilterFlags(); |
275 | 0 | if ( ( ( nFlags & nMust ) == nMust ) |
276 | 0 | && ( ( nFlags & nDont ) == SfxFilterFlags::NONE ) |
277 | 0 | ) |
278 | 0 | { |
279 | 0 | return pFilter; |
280 | 0 | } |
281 | 0 | } |
282 | 0 | } |
283 | 0 | catch( const Exception& ) |
284 | 0 | { |
285 | 0 | DBG_UNHANDLED_EXCEPTION("sfx.view"); |
286 | 0 | } |
287 | 0 | return nullptr; |
288 | 0 | } |
289 | | |
290 | | |
291 | | OUString SfxFrameLoader_Impl::impl_askForFilter_nothrow( const Reference< XInteractionHandler >& i_rxHandler, |
292 | | const OUString& i_rDocumentURL ) |
293 | 0 | { |
294 | 0 | ENSURE_OR_THROW( i_rxHandler.is(), "invalid interaction handler" ); |
295 | |
|
296 | 0 | OUString sFilterName; |
297 | 0 | try |
298 | 0 | { |
299 | 0 | ::framework::RequestFilterSelect aRequest( i_rDocumentURL ); |
300 | 0 | i_rxHandler->handle( aRequest.GetRequest() ); |
301 | 0 | if( !aRequest.isAbort() ) |
302 | 0 | sFilterName = aRequest.getFilter(); |
303 | 0 | } |
304 | 0 | catch( const Exception& ) |
305 | 0 | { |
306 | 0 | DBG_UNHANDLED_EXCEPTION("sfx.view"); |
307 | 0 | } |
308 | |
|
309 | 0 | return sFilterName; |
310 | 0 | } |
311 | | |
312 | | bool lcl_getDispatchResult(const SfxPoolItemHolder& rResult) |
313 | 0 | { |
314 | 0 | if (!rResult) |
315 | 0 | return false; |
316 | | |
317 | | // default must be set to true, because some return values |
318 | | // can't be checked, but nonetheless indicate "success"! |
319 | 0 | bool bSuccess = true; |
320 | | |
321 | | // On the other side some special slots return a boolean state, |
322 | | // which can be set to FALSE. |
323 | 0 | const SfxBoolItem* pItem(dynamic_cast<const SfxBoolItem*>(rResult.getItem())); |
324 | 0 | if ( pItem ) |
325 | 0 | bSuccess = pItem->GetValue(); |
326 | |
|
327 | 0 | return bSuccess; |
328 | 0 | } |
329 | | |
330 | | bool SfxFrameLoader_Impl::impl_createNewDocWithSlotParam( const sal_uInt16 _nSlotID, const Reference< XFrame >& i_rxFrame, |
331 | | const bool i_bHidden ) |
332 | 0 | { |
333 | 0 | SfxRequest aRequest( _nSlotID, SfxCallMode::SYNCHRON, SfxGetpApp()->GetPool() ); |
334 | 0 | aRequest.AppendItem( SfxUnoFrameItem( SID_FILLFRAME, i_rxFrame ) ); |
335 | 0 | if ( i_bHidden ) |
336 | 0 | aRequest.AppendItem( SfxBoolItem( SID_HIDDEN, true ) ); |
337 | 0 | return lcl_getDispatchResult(SfxGetpApp()->ExecuteSlot(aRequest)); |
338 | 0 | } |
339 | | |
340 | | |
341 | | void SfxFrameLoader_Impl::impl_determineFilter( ::comphelper::NamedValueCollection& io_rDescriptor ) const |
342 | 0 | { |
343 | 0 | const OUString sURL = io_rDescriptor.getOrDefault( u"URL"_ustr, OUString() ); |
344 | 0 | const OUString sTypeName = io_rDescriptor.getOrDefault( u"TypeName"_ustr, OUString() ); |
345 | 0 | const OUString sFilterName = io_rDescriptor.getOrDefault( u"FilterName"_ustr, OUString() ); |
346 | 0 | const OUString sServiceName = io_rDescriptor.getOrDefault( u"DocumentService"_ustr, OUString() ); |
347 | 0 | const Reference< XInteractionHandler > |
348 | 0 | xInteraction = io_rDescriptor.getOrDefault( u"InteractionHandler"_ustr, Reference< XInteractionHandler >() ); |
349 | |
|
350 | 0 | const SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher(); |
351 | 0 | std::shared_ptr<const SfxFilter> pFilter; |
352 | | |
353 | | // get filter by its name directly ... |
354 | 0 | if ( !sFilterName.isEmpty() ) |
355 | 0 | pFilter = rMatcher.GetFilter4FilterName( sFilterName ); |
356 | | |
357 | | // or search the preferred filter for the detected type ... |
358 | 0 | if ( !pFilter && !sTypeName.isEmpty() ) |
359 | 0 | pFilter = rMatcher.GetFilter4EA( sTypeName ); |
360 | | |
361 | | // or use given document service for detection, too |
362 | 0 | if ( !pFilter && !sServiceName.isEmpty() ) |
363 | 0 | pFilter = impl_getFilterFromServiceName_nothrow( sServiceName ); |
364 | | |
365 | | // or use interaction to ask user for right filter. |
366 | 0 | if ( !pFilter && xInteraction.is() && !sURL.isEmpty() ) |
367 | 0 | { |
368 | 0 | OUString sSelectedFilter = impl_askForFilter_nothrow( xInteraction, sURL ); |
369 | 0 | if ( !sSelectedFilter.isEmpty() ) |
370 | 0 | pFilter = rMatcher.GetFilter4FilterName( sSelectedFilter ); |
371 | 0 | } |
372 | |
|
373 | 0 | if ( !pFilter ) |
374 | 0 | return; |
375 | | |
376 | 0 | io_rDescriptor.put( u"FilterName"_ustr, pFilter->GetFilterName() ); |
377 | | |
378 | | // If detected filter indicates using of an own template format |
379 | | // add property "AsTemplate" to descriptor. But suppress this step |
380 | | // if such property already exists. |
381 | 0 | if ( pFilter->IsOwnTemplateFormat() && !io_rDescriptor.has( u"AsTemplate"_ustr ) ) |
382 | 0 | io_rDescriptor.put( u"AsTemplate"_ustr, true ); |
383 | | |
384 | | // The DocumentService property will finally be used to determine the document type to create, so |
385 | | // override it with the service name as indicated by the found filter. |
386 | 0 | io_rDescriptor.put( u"DocumentService"_ustr, pFilter->GetServiceName() ); |
387 | 0 | } |
388 | | |
389 | | |
390 | | SfxObjectShellRef SfxFrameLoader_Impl::impl_findObjectShell( const Reference< XModel2 >& i_rxDocument ) |
391 | 0 | { |
392 | 0 | for ( SfxObjectShell* pDoc = SfxObjectShell::GetFirst( nullptr, false ); pDoc; |
393 | 0 | pDoc = SfxObjectShell::GetNext( *pDoc, nullptr, false ) ) |
394 | 0 | { |
395 | 0 | if ( i_rxDocument == pDoc->GetModel() ) |
396 | 0 | { |
397 | 0 | return pDoc; |
398 | 0 | } |
399 | 0 | } |
400 | | |
401 | 0 | SAL_WARN( "sfx.view", "SfxFrameLoader_Impl::impl_findObjectShell: model is not based on SfxObjectShell - wrong frame loader usage!" ); |
402 | 0 | return nullptr; |
403 | 0 | } |
404 | | |
405 | | |
406 | | bool SfxFrameLoader_Impl::impl_determineTemplateDocument( ::comphelper::NamedValueCollection& io_rDescriptor ) const |
407 | 0 | { |
408 | 0 | try |
409 | 0 | { |
410 | 0 | const OUString sTemplateRegioName = io_rDescriptor.getOrDefault( u"TemplateRegionName"_ustr, OUString() ); |
411 | 0 | const OUString sTemplateName = io_rDescriptor.getOrDefault( u"TemplateName"_ustr, OUString() ); |
412 | 0 | const OUString sServiceName = io_rDescriptor.getOrDefault( u"DocumentService"_ustr, OUString() ); |
413 | 0 | const OUString sURL = io_rDescriptor.getOrDefault( u"URL"_ustr, OUString() ); |
414 | | |
415 | | // determine the full URL of the template to use, if any |
416 | 0 | OUString sTemplateURL; |
417 | 0 | if ( !sTemplateRegioName.isEmpty() && !sTemplateName.isEmpty() ) |
418 | 0 | { |
419 | 0 | SfxDocumentTemplates aTmpFac; |
420 | 0 | aTmpFac.GetFull( sTemplateRegioName, sTemplateName, sTemplateURL ); |
421 | 0 | } |
422 | 0 | else |
423 | 0 | { |
424 | 0 | if ( !sServiceName.isEmpty() ) |
425 | 0 | sTemplateURL = SfxObjectFactory::GetStandardTemplate( sServiceName ); |
426 | 0 | else |
427 | 0 | sTemplateURL = SfxObjectFactory::GetStandardTemplate( SfxObjectShell::GetServiceNameFromFactory( sURL ) ); |
428 | | // tdf#165851 expand trusted urls from configuration here |
429 | 0 | sTemplateURL = comphelper::getExpandedUri(m_aContext, sTemplateURL); |
430 | 0 | } |
431 | |
|
432 | 0 | if ( !sTemplateURL.isEmpty() ) |
433 | 0 | { |
434 | | // detect the filter for the template. Might still be NULL (if the template is broken, or does not |
435 | | // exist, or some such), but this is handled by our caller the same way as if no template/URL was present. |
436 | 0 | std::shared_ptr<const SfxFilter> pTemplateFilter = impl_detectFilterForURL( sTemplateURL, io_rDescriptor, SfxGetpApp()->GetFilterMatcher() ); |
437 | 0 | if ( pTemplateFilter ) |
438 | 0 | { |
439 | | // load the template document, but, well, "as template" |
440 | 0 | io_rDescriptor.put( u"FilterName"_ustr, pTemplateFilter->GetName() ); |
441 | 0 | io_rDescriptor.put( u"FileName"_ustr, sTemplateURL ); |
442 | 0 | io_rDescriptor.put( u"AsTemplate"_ustr, true ); |
443 | | |
444 | | // #i21583# |
445 | | // the DocumentService property will finally be used to create the document. Thus, override any possibly |
446 | | // present value with the document service of the template. |
447 | 0 | io_rDescriptor.put( u"DocumentService"_ustr, pTemplateFilter->GetServiceName() ); |
448 | 0 | return true; |
449 | 0 | } |
450 | 0 | } |
451 | 0 | } |
452 | 0 | catch (...) |
453 | 0 | { |
454 | 0 | } |
455 | 0 | return false; |
456 | 0 | } |
457 | | |
458 | | |
459 | | sal_uInt16 SfxFrameLoader_Impl::impl_findSlotParam( std::u16string_view i_rFactoryURL ) |
460 | 0 | { |
461 | 0 | std::u16string_view sSlotParam; |
462 | 0 | const size_t nParamPos = i_rFactoryURL.find( '?' ); |
463 | 0 | if ( nParamPos != std::u16string_view::npos ) |
464 | 0 | { |
465 | | // currently only the "slot" parameter is supported |
466 | 0 | const size_t nSlotPos = i_rFactoryURL.find( u"slot=", nParamPos ); |
467 | 0 | if ( nSlotPos > 0 && nSlotPos != std::u16string_view::npos ) |
468 | 0 | sSlotParam = i_rFactoryURL.substr( nSlotPos + 5 ); |
469 | 0 | } |
470 | |
|
471 | 0 | if ( !sSlotParam.empty() ) |
472 | 0 | return sal_uInt16( o3tl::toInt32(sSlotParam) ); |
473 | | |
474 | 0 | return 0; |
475 | 0 | } |
476 | | |
477 | | |
478 | | void SfxFrameLoader_Impl::impl_handleCaughtError_nothrow( const Any& i_rCaughtError, const ::comphelper::NamedValueCollection& i_rDescriptor ) |
479 | 0 | { |
480 | 0 | try |
481 | 0 | { |
482 | 0 | const Reference< XInteractionHandler > xInteraction = |
483 | 0 | i_rDescriptor.getOrDefault( u"InteractionHandler"_ustr, Reference< XInteractionHandler >() ); |
484 | 0 | if ( !xInteraction.is() ) |
485 | 0 | return; |
486 | 0 | ::rtl::Reference< ::comphelper::OInteractionRequest > pRequest( new ::comphelper::OInteractionRequest( i_rCaughtError ) ); |
487 | 0 | ::rtl::Reference< ::comphelper::OInteractionApprove > pApprove( new ::comphelper::OInteractionApprove ); |
488 | 0 | pRequest->addContinuation( pApprove ); |
489 | |
|
490 | 0 | const Reference< XInteractionHandler2 > xHandler( xInteraction, UNO_QUERY ); |
491 | | #if OSL_DEBUG_LEVEL > 0 |
492 | | const bool bHandled = |
493 | | #endif |
494 | 0 | xHandler.is() && xHandler->handleInteractionRequest( pRequest ); |
495 | |
|
496 | | #if OSL_DEBUG_LEVEL > 0 |
497 | | if ( !bHandled ) |
498 | | // the interaction handler couldn't deal with this error |
499 | | // => report it as assertion, at least (done in the DBG_UNHANDLED_EXCEPTION below) |
500 | | ::cppu::throwException( i_rCaughtError ); |
501 | | #endif |
502 | 0 | } |
503 | 0 | catch( const Exception& ) |
504 | 0 | { |
505 | 0 | DBG_UNHANDLED_EXCEPTION("sfx.view"); |
506 | 0 | } |
507 | 0 | } |
508 | | |
509 | | |
510 | | void SfxFrameLoader_Impl::impl_removeLoaderArguments( ::comphelper::NamedValueCollection& io_rDescriptor ) |
511 | 0 | { |
512 | | // remove the arguments which are for the loader only, and not for a call to attachResource |
513 | 0 | io_rDescriptor.remove( u"StatusIndicator"_ustr ); |
514 | 0 | io_rDescriptor.remove( u"Model"_ustr ); |
515 | 0 | } |
516 | | |
517 | | |
518 | | ::comphelper::NamedValueCollection SfxFrameLoader_Impl::impl_extractViewCreationArgs( ::comphelper::NamedValueCollection& io_rDescriptor ) |
519 | 0 | { |
520 | 0 | static const std::u16string_view sKnownViewArgs[] = { u"JumpMark", u"PickListEntry" }; |
521 | |
|
522 | 0 | ::comphelper::NamedValueCollection aViewArgs; |
523 | 0 | for (const auto& rKnownViewArg : sKnownViewArgs) |
524 | 0 | { |
525 | 0 | const OUString sKnownViewArg(rKnownViewArg); |
526 | 0 | if ( io_rDescriptor.has( sKnownViewArg ) ) |
527 | 0 | { |
528 | 0 | aViewArgs.put( sKnownViewArg, io_rDescriptor.get( sKnownViewArg ) ); |
529 | 0 | io_rDescriptor.remove( sKnownViewArg ); |
530 | 0 | } |
531 | 0 | } |
532 | 0 | return aViewArgs; |
533 | 0 | } |
534 | | |
535 | | |
536 | | SfxInterfaceId SfxFrameLoader_Impl::impl_determineEffectiveViewId_nothrow( const SfxObjectShell& i_rDocument, const ::comphelper::NamedValueCollection& i_rDescriptor ) |
537 | 0 | { |
538 | 0 | SfxInterfaceId nViewId(i_rDescriptor.getOrDefault( u"ViewId"_ustr, sal_Int16( 0 ) )); |
539 | 0 | try |
540 | 0 | { |
541 | 0 | if ( nViewId == SFX_INTERFACE_NONE ) |
542 | 0 | do |
543 | 0 | { |
544 | 0 | Reference< XViewDataSupplier > xViewDataSupplier( i_rDocument.GetModel(), UNO_QUERY ); |
545 | 0 | Reference< XIndexAccess > xViewData; |
546 | 0 | if ( xViewDataSupplier.is() ) |
547 | 0 | xViewData.set( xViewDataSupplier->getViewData() ); |
548 | |
|
549 | 0 | if ( !xViewData.is() || ( xViewData->getCount() == 0 ) ) |
550 | | // no view data stored together with the model |
551 | 0 | break; |
552 | | |
553 | | // obtain the ViewID from the view data |
554 | 0 | Sequence< PropertyValue > aViewData; |
555 | 0 | if ( !( xViewData->getByIndex( 0 ) >>= aViewData ) ) |
556 | 0 | break; |
557 | | |
558 | 0 | OUString sViewId = ::comphelper::NamedValueCollection::getOrDefault( aViewData, u"ViewId", OUString() ); |
559 | 0 | if ( sViewId.isEmpty() ) |
560 | 0 | break; |
561 | | |
562 | | // somewhat weird convention here ... in the view data, the ViewId is a string, effectively describing |
563 | | // a view name. In the document load descriptor, the ViewId is in fact the numeric ID. |
564 | | |
565 | 0 | SfxViewFactory* pViewFactory = i_rDocument.GetFactory().GetViewFactoryByViewName( sViewId ); |
566 | 0 | if ( pViewFactory ) |
567 | 0 | nViewId = pViewFactory->GetOrdinal(); |
568 | 0 | } |
569 | 0 | while ( false ); |
570 | 0 | } |
571 | 0 | catch( const Exception& ) |
572 | 0 | { |
573 | 0 | DBG_UNHANDLED_EXCEPTION("sfx.view"); |
574 | 0 | } |
575 | |
|
576 | 0 | if ( nViewId == SFX_INTERFACE_NONE ) |
577 | 0 | nViewId = i_rDocument.GetFactory().GetViewFactory().GetOrdinal(); |
578 | 0 | return nViewId; |
579 | 0 | } |
580 | | |
581 | | |
582 | | Reference< XController2 > SfxFrameLoader_Impl::impl_createDocumentView( const Reference< XModel2 >& i_rModel, |
583 | | const Reference< XFrame >& i_rFrame, const ::comphelper::NamedValueCollection& i_rViewFactoryArgs, |
584 | | const OUString& i_rViewName ) |
585 | 0 | { |
586 | | // let the model create a new controller |
587 | 0 | const Reference< XController2 > xController( i_rModel->createViewController( |
588 | 0 | i_rViewName, |
589 | 0 | i_rViewFactoryArgs.getPropertyValues(), |
590 | 0 | i_rFrame |
591 | 0 | ), UNO_SET_THROW ); |
592 | | |
593 | | // introduce model/view/controller to each other |
594 | 0 | utl::ConnectFrameControllerModel(i_rFrame, xController, i_rModel); |
595 | |
|
596 | 0 | return xController; |
597 | 0 | } |
598 | | |
599 | | std::shared_ptr<const SfxFilter> getEmptyURLFilter(std::u16string_view sURL) |
600 | 0 | { |
601 | 0 | INetURLObject aParser(sURL); |
602 | 0 | const OUString aExt = aParser.getExtension(INetURLObject::LAST_SEGMENT, true, |
603 | 0 | INetURLObject::DecodeMechanism::WithCharset); |
604 | 0 | const SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher(); |
605 | | |
606 | | // Requiring the export+preferred flags helps to find the relevant filter, e.g. .doc -> WW8 (and |
607 | | // not WW6 or Mac_Word). |
608 | 0 | std::shared_ptr<const SfxFilter> pFilter = rMatcher.GetFilter4Extension( |
609 | 0 | aExt, SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT | SfxFilterFlags::PREFERED); |
610 | 0 | if (!pFilter) |
611 | 0 | { |
612 | | // retry without PREFERED so we can find at least something for 0-byte *.ods |
613 | 0 | pFilter |
614 | 0 | = rMatcher.GetFilter4Extension(aExt, SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT); |
615 | 0 | } |
616 | 0 | return pFilter; |
617 | 0 | } |
618 | | |
619 | | sal_Bool SAL_CALL SfxFrameLoader_Impl::load( const Sequence< PropertyValue >& rArgs, |
620 | | const Reference< XFrame >& _rTargetFrame ) |
621 | 0 | { |
622 | 0 | ENSURE_OR_THROW( _rTargetFrame.is(), "illegal NULL frame" ); |
623 | |
|
624 | 0 | SAL_INFO( "sfx.view", "SfxFrameLoader::load" ); |
625 | | |
626 | 0 | ::comphelper::NamedValueCollection aDescriptor( rArgs ); |
627 | | |
628 | | // ensure the descriptor contains a referrer |
629 | 0 | if ( !aDescriptor.has( u"Referer"_ustr ) ) |
630 | 0 | aDescriptor.put( u"Referer"_ustr, OUString() ); |
631 | | |
632 | | // did the caller already pass a model? |
633 | 0 | Reference< XModel2 > xModel = aDescriptor.getOrDefault( u"Model"_ustr, Reference< XModel2 >() ); |
634 | 0 | const bool bExternalModel = xModel.is(); |
635 | |
|
636 | | #if ENABLE_YRS |
637 | | uno::Reference<connection::XConnection> xConnection; |
638 | | if (!xModel.is() |
639 | | && aDescriptor.getOrDefault(u"URL"_ustr, OUString()) == "private:factory/swriter" |
640 | | && getenv("YRSCONNECT")) |
641 | | { |
642 | | SAL_INFO("sfx.yrs", "YRS connect sfx2"); |
643 | | |
644 | | // must read this SYNC |
645 | | auto const conn = u"pipe,name=ytest"_ustr; |
646 | | auto const xConnector = css::connection::Connector::create(m_aContext); |
647 | | xConnection = xConnector->connect(conn); |
648 | | uno::Sequence<sal_Int8> buf; |
649 | | if (xConnection->read(buf, 4) != 4) |
650 | | { |
651 | | abort(); |
652 | | } |
653 | | sal_Int32 const size{static_cast<sal_uInt8>(buf[0]) |
654 | | | static_cast<sal_uInt8>(buf[1]) << 8 |
655 | | | static_cast<sal_uInt8>(buf[2]) << 16 |
656 | | | static_cast<sal_uInt8>(buf[3]) << 24}; |
657 | | if (size != 0) |
658 | | { |
659 | | SAL_INFO("sfx.yrs", "YRS connect reading file of size " << size); |
660 | | uno::Sequence<sal_Int8> buff(size); |
661 | | if (xConnection->read(buff, size) != size) |
662 | | { |
663 | | abort(); |
664 | | } |
665 | | uno::Reference<io::XInputStream> const xInStream{ |
666 | | io::SequenceInputStream::createStreamFromSequence(m_aContext, buff)}; |
667 | | assert(xInStream.is()); |
668 | | |
669 | | aDescriptor.put(u"URL"_ustr, u"private:stream"_ustr); |
670 | | aDescriptor.put(u"InputStream"_ustr, uno::Any(xInStream)); |
671 | | } |
672 | | aDescriptor.put(u"ReadOnly"_ustr, uno::Any(true)); |
673 | | aDescriptor.put(u"YrsConnect"_ustr, uno::Any(xConnection)); |
674 | | } |
675 | | #endif |
676 | | |
677 | | // check for factory URLs to create a new doc, instead of loading one |
678 | 0 | const OUString sURL = aDescriptor.getOrDefault( u"URL"_ustr, OUString() ); |
679 | 0 | const bool bIsFactoryURL = sURL.startsWith( "private:factory/" ); |
680 | |
|
681 | 0 | if (bIsFactoryURL && officecfg::Office::Common::Misc::ViewerAppMode::get()) |
682 | 0 | return false; |
683 | | |
684 | 0 | std::shared_ptr<const SfxFilter> pEmptyURLFilter; |
685 | 0 | bool bInitNewModel = bIsFactoryURL; |
686 | 0 | const bool bIsDefault = bIsFactoryURL && !bExternalModel; |
687 | 0 | if (!aDescriptor.has(u"Replaceable"_ustr)) |
688 | 0 | aDescriptor.put(u"Replaceable"_ustr, bIsDefault); |
689 | 0 | if (bIsDefault) |
690 | 0 | { |
691 | 0 | const OUString sFactory = sURL.copy( sizeof( "private:factory/" ) -1 ); |
692 | | // special handling for some weird factory URLs a la private:factory/swriter?slot=21053 |
693 | 0 | const sal_uInt16 nSlotParam = impl_findSlotParam( sFactory ); |
694 | 0 | if ( nSlotParam != 0 ) |
695 | 0 | { |
696 | 0 | return impl_createNewDocWithSlotParam( nSlotParam, _rTargetFrame, aDescriptor.getOrDefault( u"Hidden"_ustr, false ) ); |
697 | 0 | } |
698 | | |
699 | 0 | const bool bDescribesValidTemplate = impl_determineTemplateDocument( aDescriptor ); |
700 | 0 | if ( bDescribesValidTemplate ) |
701 | 0 | { |
702 | | // if the media descriptor allowed us to determine a template document to create the new document |
703 | | // from, then do not init a new document model from scratch (below), but instead load the |
704 | | // template document |
705 | 0 | bInitNewModel = false; |
706 | 0 | } |
707 | 0 | else |
708 | 0 | { |
709 | 0 | const OUString sServiceName = SfxObjectShell::GetServiceNameFromFactory( sFactory ); |
710 | 0 | aDescriptor.put( u"DocumentService"_ustr, sServiceName ); |
711 | 0 | } |
712 | 0 | } |
713 | 0 | else |
714 | 0 | { |
715 | | // compatibility |
716 | 0 | aDescriptor.put( u"FileName"_ustr, aDescriptor.get( u"URL"_ustr ) ); |
717 | |
|
718 | 0 | if (!bIsFactoryURL && !bExternalModel && tools::isEmptyFileUrl(sURL)) |
719 | 0 | { |
720 | 0 | pEmptyURLFilter = getEmptyURLFilter(sURL); |
721 | 0 | if (pEmptyURLFilter) |
722 | 0 | { |
723 | 0 | aDescriptor.put(u"DocumentService"_ustr, pEmptyURLFilter->GetServiceName()); |
724 | 0 | if (impl_determineTemplateDocument(aDescriptor)) |
725 | 0 | { |
726 | | // if the media descriptor allowed us to determine a template document |
727 | | // to create the new document from, then do not init a new document model |
728 | | // from scratch (below), but instead load the template document |
729 | 0 | bInitNewModel = false; |
730 | | // Do not try to load from empty UCB content |
731 | 0 | aDescriptor.remove(u"UCBContent"_ustr); |
732 | 0 | } |
733 | 0 | else |
734 | 0 | { |
735 | 0 | bInitNewModel = true; |
736 | 0 | } |
737 | 0 | } |
738 | 0 | } |
739 | 0 | } |
740 | | |
741 | 0 | bool bLoadSuccess = false; |
742 | 0 | try |
743 | 0 | { |
744 | | // extract view relevant arguments from the loader args |
745 | 0 | ::comphelper::NamedValueCollection aViewCreationArgs( impl_extractViewCreationArgs( aDescriptor ) ); |
746 | | |
747 | | // no model passed from outside? => create one from scratch |
748 | 0 | if ( !bExternalModel ) |
749 | 0 | { |
750 | 0 | bool bInternalFilter = aDescriptor.getOrDefault<OUString>(u"FilterProvider"_ustr, OUString()).isEmpty(); |
751 | |
|
752 | 0 | if (bInternalFilter && !bInitNewModel) |
753 | 0 | { |
754 | | // Ensure that the current SfxFilter instance is loaded before |
755 | | // going further. We don't need to do this for external |
756 | | // filter providers. |
757 | 0 | impl_determineFilter(aDescriptor); |
758 | 0 | } |
759 | | |
760 | | // create the new doc |
761 | 0 | const OUString sServiceName = aDescriptor.getOrDefault( u"DocumentService"_ustr, OUString() ); |
762 | 0 | xModel.set( m_aContext->getServiceManager()->createInstanceWithContext(sServiceName, m_aContext), UNO_QUERY_THROW ); |
763 | | |
764 | | // load resp. init it |
765 | 0 | const Reference< XLoadable > xLoadable( xModel, UNO_QUERY_THROW ); |
766 | 0 | if ( bInitNewModel ) |
767 | 0 | { |
768 | 0 | xLoadable->initNew(); |
769 | |
|
770 | 0 | impl_removeLoaderArguments( aDescriptor ); |
771 | 0 | xModel->attachResource( OUString(), aDescriptor.getPropertyValues() ); |
772 | 0 | } |
773 | 0 | else |
774 | 0 | { |
775 | 0 | xLoadable->load( aDescriptor.getPropertyValues() ); |
776 | 0 | } |
777 | 0 | } |
778 | 0 | else |
779 | 0 | { |
780 | | // tell the doc its (current) load args. |
781 | 0 | impl_removeLoaderArguments( aDescriptor ); |
782 | 0 | xModel->attachResource( xModel->getURL(), aDescriptor.getPropertyValues() ); |
783 | 0 | } |
784 | |
|
785 | 0 | SolarMutexGuard aGuard; |
786 | | |
787 | | // get the SfxObjectShell (still needed at the moment) |
788 | | // SfxObjectShellRef is used here ( instead of ...Lock ) since the model is closed below if necessary |
789 | | // SfxObjectShellLock would be even dangerous here, since the lifetime control should be done outside in case of success |
790 | 0 | const SfxObjectShellRef xDoc = impl_findObjectShell( xModel ); |
791 | 0 | ENSURE_OR_THROW( xDoc.is(), "no SfxObjectShell for the given model" ); |
792 | |
|
793 | 0 | if (pEmptyURLFilter) |
794 | 0 | { |
795 | | // Detach the medium from the template, and set proper document name and filter |
796 | 0 | auto pMedium = xDoc->GetMedium(); |
797 | 0 | auto& rItemSet = pMedium->GetItemSet(); |
798 | 0 | rItemSet.ClearItem(SID_TEMPLATE); |
799 | 0 | rItemSet.Put(SfxStringItem(SID_FILTER_NAME, pEmptyURLFilter->GetFilterName())); |
800 | 0 | pMedium->SetName(sURL, true); |
801 | 0 | pMedium->SetFilter(pEmptyURLFilter); |
802 | 0 | pMedium->GetInitFileDate(true); |
803 | 0 | xDoc->SetLoading(SfxLoadedFlags::NONE); |
804 | 0 | xDoc->FinishedLoading(); |
805 | 0 | } |
806 | | |
807 | | // ensure the ID of the to-be-created view is in the descriptor, if possible |
808 | 0 | const SfxInterfaceId nViewId = impl_determineEffectiveViewId_nothrow( *xDoc, aDescriptor ); |
809 | 0 | const sal_Int16 nViewNo = xDoc->GetFactory().GetViewNo_Impl( nViewId, 0 ); |
810 | 0 | const OUString sViewName( xDoc->GetFactory().GetViewFactory( nViewNo ).GetAPIViewName() ); |
811 | | |
812 | | // plug the document into the frame |
813 | 0 | Reference<XController2> xController = |
814 | 0 | impl_createDocumentView( xModel, _rTargetFrame, aViewCreationArgs, sViewName ); |
815 | |
|
816 | 0 | Reference<lang::XInitialization> xInit(xController, UNO_QUERY); |
817 | 0 | if (xInit.is()) |
818 | 0 | { |
819 | 0 | uno::Sequence<uno::Any> aArgs; // empty for now. |
820 | 0 | xInit->initialize(aArgs); |
821 | 0 | } |
822 | |
|
823 | 0 | bLoadSuccess = true; |
824 | 0 | } |
825 | 0 | catch ( Exception& ) |
826 | 0 | { |
827 | 0 | const Any aError( ::cppu::getCaughtException() ); |
828 | 0 | if ( !aDescriptor.getOrDefault( u"Silent"_ustr, false ) ) |
829 | 0 | impl_handleCaughtError_nothrow( aError, aDescriptor ); |
830 | 0 | } |
831 | | |
832 | | // if loading was not successful, close the document |
833 | 0 | if ( !bLoadSuccess && !bExternalModel ) |
834 | 0 | { |
835 | 0 | try |
836 | 0 | { |
837 | 0 | const Reference< XCloseable > xCloseable( xModel, UNO_QUERY_THROW ); |
838 | 0 | xCloseable->close( true ); |
839 | 0 | } |
840 | 0 | catch ( Exception& ) |
841 | 0 | { |
842 | 0 | DBG_UNHANDLED_EXCEPTION("sfx.view"); |
843 | 0 | } |
844 | 0 | } |
845 | |
|
846 | 0 | return bLoadSuccess; |
847 | 0 | } |
848 | | |
849 | | void SfxFrameLoader_Impl::cancel() |
850 | 0 | { |
851 | 0 | } |
852 | | |
853 | | /* XServiceInfo */ |
854 | | OUString SAL_CALL SfxFrameLoader_Impl::getImplementationName() |
855 | 0 | { |
856 | 0 | return u"com.sun.star.comp.office.FrameLoader"_ustr; |
857 | 0 | } |
858 | | |
859 | | /* XServiceInfo */ |
860 | | sal_Bool SAL_CALL SfxFrameLoader_Impl::supportsService( const OUString& sServiceName ) |
861 | 0 | { |
862 | 0 | return cppu::supportsService(this, sServiceName); |
863 | 0 | } |
864 | | |
865 | | /* XServiceInfo */ |
866 | | Sequence< OUString > SAL_CALL SfxFrameLoader_Impl::getSupportedServiceNames() |
867 | 0 | { |
868 | 0 | return { u"com.sun.star.frame.SynchronousFrameLoader"_ustr, u"com.sun.star.frame.OfficeFrameLoader"_ustr }; |
869 | 0 | } |
870 | | |
871 | | } |
872 | | |
873 | | extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * |
874 | | com_sun_star_comp_office_FrameLoader_get_implementation( |
875 | | css::uno::XComponentContext *context, |
876 | | css::uno::Sequence<css::uno::Any> const &) |
877 | 0 | { |
878 | 0 | return cppu::acquire(new SfxFrameLoader_Impl(context)); |
879 | 0 | } |
880 | | |
881 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |