11Programmatic Save As API
2-
2+ ===
33
44# Background
55
@@ -15,6 +15,253 @@ Additionally, we propose the `SaveAsRequestedEvent`. You can register this event
1515
1616# Examples
1717## Win32 C++
18+ ``` c++
19+ // ! [ToggleSilent]
20+ // Turn on/off Silent SaveAs, which won't show the system default save as dialog.
21+ // This example hides the default save as dialog and shows a customized dialog.
22+ bool ScenarioSaveAs::ToggleSilent ()
23+ {
24+ if (!m_webView2Staging20)
25+ return false;
26+ if (m_silentSaveAs)
27+ {
28+ // Unregister the handler for the `SaveAsRequested` event.
29+ m_webView2Staging20->remove_SaveAsRequested(m_saveAsRequestedToken);
30+ }
31+ else
32+ {
33+ // Register a handler for the `SaveAsRequested` event.
34+ m_webView2Staging20->add_SaveAsRequested(
35+ Callback<ICoreWebView2StagingSaveAsRequestedEventHandler>(
36+ [this](
37+ ICoreWebView2* sender,
38+ ICoreWebView2StagingSaveAsRequestedEventArgs* args) -> HRESULT
39+ {
40+ // Hide the system default save as dialog.
41+ CHECK_FAILURE (args->put_Handled(TRUE));
42+
43+ auto showCustomizedDialog = [this, args]
44+ {
45+ // Preview the content mime type, optional
46+ wil::unique_cotaskmem_string mimeType;
47+ CHECK_FAILURE (args->get_ContentMimeType(&mimeType));
48+
49+ SaveAsDialog dialog(m_appWindow->GetMainWindow(), contentSaveTypes);
50+ if (dialog.confirmed)
51+ {
52+ // Set the ResultFilePath, SaveAsType, AllowReplace for the event
53+ // args from this customized dialog inputs, optional. If nothing
54+ // needs to input, the event args will provide default values.
55+ CHECK_FAILURE (
56+ args->put_ResultFilePath((LPCWSTR)dialog.path.c_str()));
57+ CHECK_FAILURE (args->put_SaveAsType(dialog.selectedType));
58+ CHECK_FAILURE(args->put_AllowReplace(dialog.allowReplace));
59+
60+ // Confirm to download, required
61+ CHECK_FAILURE (args->put_ConfirmToSave(TRUE));
62+ }
63+ };
64+
65+ wil::com_ptr<ICoreWebView2Deferral> deferral;
66+ CHECK_FAILURE (args->GetDeferral(&deferral));
67+
68+ m_appWindow->RunAsync(
69+ [deferral, showCustomizedDialog]()
70+ {
71+ showCustomizedDialog ();
72+ CHECK_FAILURE(deferral->Complete());
73+ });
74+ return S_OK;
75+ })
76+ .Get(),
77+ &m_saveAsRequestedToken);
78+ }
79+ m_silentSaveAs = !m_silentSaveAs;
80+ MessageBox(
81+ m_appWindow->GetMainWindow(),
82+ (m_silentSaveAs ? L"Silent Save As Enabled" : L"Silent Save As Disabled"), L"Info",
83+ MB_OK);
84+ return true;
85+ }
86+ // ! [ToggleSilent]
87+
88+ // ! [ProgrammaticSaveAs]
89+ // Call SaveContentAs method to trigger the programmatic save as.
90+ bool ScenarioSaveAs::ProgrammaticSaveAs ()
91+ {
92+ if (!m_webView2Staging20)
93+ return false;
94+ m_webView2Staging20->SaveContentAs(
95+ Callback<ICoreWebView2StagingSaveContentAsCompletedHandler>(
96+ [this](HRESULT errorCode, COREWEBVIEW2_SAVE_CONTENT_AS_RESULTS result) -> HRESULT
97+ {
98+ // Show SaveContentAs returned result, optional
99+ // Skip message box when the result is COREWEBVIEW2_SAVE_AS_OPEN_SYSTEM_DIALOG (0),
100+ // to prevent it deactivates save as modal dialog focus on the window
101+ if (result > 0)
102+ {
103+ MessageBox (
104+ m_appWindow->GetMainWindow(),
105+ (L"Save As " + saveAsResultString[result]).c_str(), L"Info", MB_OK);
106+ }
107+ return S_OK;
108+ })
109+ .Get());
110+ return true;
111+ }
112+ // ! [ProgrammaticSaveAs]
113+ ```
18114# API Details
19115## Win32 C++
20- Sync with the code and design doc soon
116+ ``` c++
117+ // / Specifies save as type selection options for `ICoreWebView2Staging20`,
118+ // / used in `SaveAsRequestedEventArgs`
119+ // /
120+ // / When the source is a html page, allows to select `HTML_ONLY`,
121+ // / `SINGLE_FILE`, `COMPLETE`; when the source is a non-html,
122+ // / only allows to select `DEFAULT`; otherwise, will deny the download
123+ // / and return `COREWEBVIEW2_SAVE_AS_TYPE_NOT_SUPPORTED`.
124+ // /
125+ // / The content type/format is a MIME type, indicated by the source
126+ // / server side and identified by the browser. It’s not related to the
127+ // / file’s type or extension. MIME type of `text/html`,
128+ // / `application/xhtml+xml` are considered as html page.
129+ [v1_enum] typedef enum COREWEBVIEW2_SAVE_AS_TYPE {
130+ /// Default to save for a non-html content. If it is selected for a html
131+ /// page, it’s same as HTML_ONLY option.
132+ COREWEBVIEW2_SAVE_AS_TYPE_DEFAULT,
133+ /// Save the page as html
134+ COREWEBVIEW2_SAVE_AS_TYPE_HTML_ONLY,
135+ /// Save the page as mhtml
136+ COREWEBVIEW2_SAVE_AS_TYPE_SINGLE_FILE,
137+ /// Save the page as html, plus, download the page related source files in
138+ /// a folder
139+ COREWEBVIEW2_SAVE_AS_TYPE_COMPLETE,
140+ } COREWEBVIEW2_SAVE_AS_TYPE;
141+
142+ // / Status of a programmatic save as call, indicates the result
143+ // / for method `SaveContentAs`
144+ [v1_enum] typedef enum COREWEBVIEW2_SAVE_CONTENT_AS_RESULTS {
145+ /// Programmatically open a system default save as dialog
146+ COREWEBVIEW2_SAVE_AS_OPEN_SYSTEM_DIALOG,
147+ /// Save as downloading not start as given an invalid path
148+ COREWEBVIEW2_SAVE_AS_INVALID_PATH,
149+ /// Save as downloading not start as given a duplicate filename and
150+ /// replace file not allowed
151+ COREWEBVIEW2_SAVE_AS_FILE_ALREADY_EXISTS,
152+ /// Save as downloading not start as the ` SAVE_AS_TYPE ` selection not
153+ /// supported because of the content MIME type or system limits
154+ COREWEBVIEW2_SAVE_AS_TYPE_NOT_SUPPORTED,
155+ /// Cancel the save as request
156+ COREWEBVIEW2_SAVE_AS_CANCELLED,
157+ /// Save as request complete, the downloading started
158+ COREWEBVIEW2_SAVE_AS_STARTED
159+ } COREWEBVIEW2_SAVE_CONTENT_AS_RESULTS;
160+
161+
162+ [uuid(15e1c6a3-c72a-4df3-91d7-d097fbec3bfd), object, pointer_default(unique)]
163+ interface ICoreWebView2Staging20 : IUnknown {
164+ /// Programmatically trigger a save as action for current content.
165+ ///
166+ /// Opens a system modal dialog by default. Returns COREWEBVIEW2_SAVE_AS_OPEN_SYSTEM_DIALOG.
167+ /// If it was already opened, this method would not open another one
168+ ///
169+ /// If the silent save as option is enabled, won't open the system dialog, will
170+ /// raise the ` SaveAsRequested ` event instead and process through its event args. The method can return
171+ /// a detailed info to indicate the call's result. Please see COREWEBVIEW2_SAVE_CONTENT_AS_RESULTS
172+ ///
173+ /// \snippet ScenarioSaveAs.cpp ProgrammaticSaveAs
174+ HRESULT SaveContentAs([ in] ICoreWebView2StagingSaveContentAsCompletedHandler* handler);
175+
176+ /// Add an event handler for the ` SaveAsRequested ` event. This event is raised
177+ /// when save as is triggered, programmatically or manually.
178+ ///
179+ /// \snippet ScenarioSaveAs.cpp ToggleSilent
180+ HRESULT add_SaveAsRequested(
181+ [ in] ICoreWebView2StagingSaveAsRequestedEventHandler* eventHanlder,
182+ [ out] EventRegistrationToken* token);
183+
184+ /// Remove an event handler previously added with ` add_SaveAsRequested ` .
185+ ///
186+ /// \snippet ScenarioSaveAs.cpp ToggleSilent
187+ HRESULT remove_SaveAsRequested(
188+ [ in] EventRegistrationToken token);
189+ }
190+
191+ // / The event handler for the `SaveAsRequested` event, when the handler
192+ // / exists, the silent save as enables.
193+ [uuid(55b86cd2-adfd-47f1-9cef-cdfb8c414ed3), object, pointer_default(unique)]
194+ interface ICoreWebView2StagingSaveAsRequestedEventHandler : IUnknown {
195+ HRESULT Invoke(
196+ [ in] ICoreWebView2* sender,
197+ [ in] ICoreWebView2StagingSaveAsRequestedEventArgs* args);
198+ }
199+
200+ // / The event args for `SaveAsRequested` event
201+ [uuid(80101027 -b8c3-49a1-a052-9ea4bd63ba47), object, pointer_default(unique)]
202+ interface ICoreWebView2StagingSaveAsRequestedEventArgs : IUnknown {
203+ /// Get the Mime type of content to be saved
204+ [ propget] HRESULT ContentMimeType([ out, retval] LPWSTR* value);
205+
206+ /// Indicates if this event is a silent save as job. ` Handled ` as FALSE means
207+ /// save as handled by system default dialog; TRUE means a silent save as,
208+ /// will skip the system dialog.
209+ ///
210+ /// Set the ` Handled ` for save as
211+ [ propput] HRESULT Handled ([ in] BOOL handled);
212+
213+ /// Get the ` Handled ` for save as
214+ [ propget] HRESULT Handled ([ out, retval] BOOL* handled);
215+
216+ /// Indicates if a silent save as confirm to download, TRUE means confirm.
217+ /// when the event is invoked, the download will start. A programmatic call will
218+ /// return COREWEBVIEW2_SAVE_AS_STARTED as well. set it FASLE to cancel save as
219+ /// and will return COREWEBVIEW2_SAVE_AS_CANCELLED.
220+ ///
221+ /// Set the ` ConfrimToSave ` for save as
222+ [ propput] HRESULT ConfirmToSave ([ in] BOOL confirmToSave);
223+
224+ /// Get the ` ConfrimToSave ` for save as
225+ [ propget] HRESULT ConfirmToSave ([ out, retval] BOOL* confirmToSave);
226+
227+ /// Returns an ` ICoreWebView2Deferral ` object. Use this operation to complete
228+ /// the SaveAsRequestedEvent.
229+ HRESULT GetDeferral([ out, retval] ICoreWebView2Deferral** deferral);
230+
231+ /// ` ResultFilePath ` is absolute full path of the location. It includes the
232+ /// file name and extension. If ` ResultFilePath ` is not valid, e.g. root drive
233+ /// not exist, save as will be denied and return COREWEBVIEW2_SAVE_AS_INVALID_PATH.
234+ ///
235+ /// Set the ` ResultFilePath ` for save as
236+ [ propput] HRESULT ResultFilePath ([ in] LPCWSTR resultFilePath);
237+
238+ /// Get the ` ResultFilePath ` for save as
239+ [ propget] HRESULT ResultFilePath ([ out, retval] LPWSTR* resultFilePath);
240+
241+ /// ` AllowReplace ` gives the user an option to control when the file name
242+ /// duplicates with an existing file. TRUE allows the old file be replaced.
243+ /// FALSE denies when the file name duplicates, the download won’t start,
244+ /// will return COREWEBVIEW2_SAVE_AS_FILE_ALREADY_EXISTS.
245+ ///
246+ /// Set if allowed to replace the old file if duplicate happens in the save as job
247+ [ propput] HRESULT AllowReplace ([ in] BOOL allowReplace);
248+
249+ /// Get the duplicates replace rule for save as
250+ [ propget] HRESULT AllowReplace ([ out, retval] BOOL* allowReplace);
251+
252+ /// ` SaveAsType ` is required, see the enum COREWEBVIEW2_SAVE_AS_TYPE, if the type
253+ /// doesn’t match, return COREWEBVIEW2_SAVE_AS_TYPE_NOT_SUPPORT
254+ ///
255+ /// Set the content save as type for save as job
256+ [ propput] HRESULT SaveAsType ([ in] COREWEBVIEW2_SAVE_AS_TYPE type);
257+
258+ /// Get the content save as type for save as job
259+ [ propget] HRESULT SaveAsType ([ out, retval] COREWEBVIEW2_SAVE_AS_TYPE* type);
260+ }
261+
262+ // / Receive the result for `SaveContentAs` method
263+ [uuid(1a02e9d9-14d3-41c6-9581 -8d6e1e6f50fe), object, pointer_default(unique)]
264+ interface ICoreWebView2StagingSaveContentAsCompletedHandler : IUnknown {
265+ HRESULT Invoke([ in] HRESULT errorCode, [ in] COREWEBVIEW2_SAVE_CONTENT_AS_RESULTS result);
266+ }
267+ ```
0 commit comments