Skip to content

Commit 06a199f

Browse files
Add win32 example and api detail
1 parent 52febea commit 06a199f

1 file changed

Lines changed: 249 additions & 2 deletions

File tree

specs/ProgrammaticSaveAs.md

Lines changed: 249 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Programmatic 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

Comments
 (0)