Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Cet échantillon, WebView2Browser, est un navigateur web créé avec le contrôle WebView2 de Microsoft Edge .
Cet exemple dispose de son propre référentiel dédié.
- Nom de l’échantillon : WebView2Browser
- Référentiel : WebView2Browser
- Fichier solution :
WebViewBrowserApp.sln
WebView2Browser est un exemple d’application de bureau Windows qui démontre les fonctionnalités du contrôle WebView2. L’exemple d’application WebView2Browser utilise plusieurs instances WebView2.
Cet exemple est conçu comme un projet Win32 Visual Studio 2019 . Il utilise C++ et JavaScript dans l’environnement WebView2.
WebView2Browser montre certaines des utilisations les plus simples de WebView2, telles que la création et la navigation dans un WebView, mais aussi des flux de travail plus complexes tels que l’utilisation de l’API PostWebMessageAsJson pour communiquer entre les contrôles WebView2 dans des environnements distincts. Il s’agit d’un exemple de code enrichi pour montrer comment vous pouvez utiliser les API WebView2 pour créer votre propre application.
Étape 1 : Installer Visual Studio
- Installez Visual Studio, y compris la prise en charge de C++.
Étape 2 : Cloner le dépôt WebView2Samples
- Clonez (ou téléchargez en tant que
.zip) le référentiel WebView2Samples . Consultez Cloner le référentiel WebView2Samples dans Configurer votre environnement de développement pour WebView2.
Étape 3 : Ouvrir la solution dans Visual Studio
Ouvrez la solution dans Visual Studio 2019. Le SDK WebView2 est déjà inclus en tant que package NuGet dans le projet. Si vous souhaitez utiliser Visual Studio 2017, modifiez l’ensemble d’outils de plateforme du projet dans Propriétés de configuration des propriétés > du projet Ensemble > d’outils de plateforme général>. Vous devrez peut-être également remplacer le SDK Windows par la dernière version.
Apportez les modifications répertoriées ci-dessous, si vous utilisez une version de Windows inférieure à Windows 10.
Utilisation de versions antérieures à Windows 10
Si vous souhaitez créer et exécuter le navigateur dans les versions de Windows antérieures à Windows 10, apportez les modifications suivantes. Cela est nécessaire en raison de la façon dont la résolution est gérée dans Windows 10 par rapport aux versions précédentes de Windows.
- Si vous souhaitez créer et exécuter le navigateur dans les versions de Windows antérieures à Windows 10 : Dans
WebViewBrowserApp.cpp, remplacezSetProcessDpiAwarenessContextparSetProcessDPIAware:
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// Call SetProcessDPIAware() instead when using Windows 7 or any version
// below 1703 (Windows 10).
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
BrowserWindow::RegisterClass(hInstance);
// ...
- Si vous souhaitez créer et exécuter le navigateur dans les versions de Windows antérieures à Windows 10 : Dans
BrowserWindow.cpp, supprimez ou commentez l’appel suivant àGetDpiForWindow:
int BrowserWindow::GetDPIAwareBound(int bound)
{
// Remove the GetDpiForWindow call when using Windows 7 or any version
// below 1607 (Windows 10). You will also have to make sure the build
// directory is clean before building again.
return (bound * GetDpiForWindow(m_hWnd) / DEFAULT_DPI);
}
Étape 4 : Créer et exécuter l’application
Définissez la cible que vous souhaitez générer (par exemple, Déboguer ou Version, en ciblant x86 ou x64).
Générez la solution.
Exécuter (ou déboguer) l’application.
Fermez l’application.
Étape 5 : Mettre à jour le SDK WebView2
- Mettez à jour la version du SDK WebView2, dans Visual Studio. Pour ce faire, cliquez avec le bouton droit sur le projet, puis cliquez sur Gérer les packages NuGet.
Étape 6 : Construisez et exécutez l’application avec le SDK WebView2 mis à jour
- Générez et exécutez à nouveau l’application.
Disposition du navigateur
L’exemple d’application WebView2Browser utilise plusieurs instances WebView2.
WebView2Browser a une approche multi-WebView pour intégrer le contenu web et l’interface utilisateur des applications dans une application de bureau Windows. Cela permet au navigateur d’utiliser les technologies Web standard (HTML, CSS, JavaScript) pour allumer l’interface, mais permet également à l’application de récupérer des favicons sur le Web et d’utiliser IndexedDB pour stocker les favoris et l’historique.
L’approche multi-WebView implique l’utilisation de deux environnements WebView distincts (chacun avec son propre répertoire de données utilisateur) : l’un pour les WebViews d’interface utilisateur et l’autre pour tous les WebViews de contenu. Les WebViews de l’interface utilisateur (liste déroulante des contrôles et des options) utilisent l’environnement d’interface utilisateur, tandis que les WebViews de contenu Web (une par onglet) utilisent l’environnement de contenu.
Fonctionnalités
L’exemple WebView2Browser fournit toutes les fonctionnalités nécessaires pour créer un navigateur web de base, mais vous avez de la place pour jouer.
L’exemple WebView2Browser implémente les fonctionnalités suivantes :
- Retour/avant
- Recharger la page
- Annuler la navigation
- Plusieurs onglets
- Historique
- Favoris
- Rechercher à partir de la barre d’adresse
- status de sécurité de la page
- Suppression du cache et des cookies
API WebView2
WebView2Browser utilise une poignée d’API disponibles dans WebView2. Pour les API qui ne sont pas utilisées ici, vous trouverez plus d’informations à leur sujet dans la référence Microsoft Edge WebView2. Voici une liste des API les plus intéressantes utilisées par WebView2Browser et des fonctionnalités qu’elles activent.
| API | Fonctionnalités |
|---|---|
CreateCoreWebView2EnvironmentWithOptions |
Utilisé pour créer les environnements des affichages web de l’interface utilisateur et du contenu. Différents répertoires de données utilisateur sont passés pour isoler l’interface utilisateur du contenu web. |
ICoreWebView2 |
Il existe plusieurs WebViews dans WebView2Browser et la plupart des fonctionnalités utilisent les membres de cette interface, le tableau ci-dessous montre comment ils sont utilisés. |
ICoreWebView2DevToolsProtocolEventReceivedEventHandler |
Utilisé avec add_DevToolsProtocolEventReceived pour écouter les événements de sécurité CDP afin de mettre à jour l’icône de verrouillage dans l’interface utilisateur du navigateur. |
ICoreWebView2DevToolsProtocolEventReceiver |
Utilisé avec add_DevToolsProtocolEventReceived pour écouter les événements de sécurité CDP afin de mettre à jour l’icône de verrouillage dans l’interface utilisateur du navigateur. |
ICoreWebView2ExecuteScriptCompletedHandler |
Utilisé avec ExecuteScript pour obtenir le titre et le favicon de la page visitée. |
ICoreWebView2FocusChangedEventHandler |
Utilisé avec add_LostFocus pour masquer la liste déroulante des options du navigateur lorsqu’elle perd le focus. |
ICoreWebView2HistoryChangedEventHandler |
Utilisé avec add_HistoryChanged pour mettre à jour les boutons de navigation dans l’interface utilisateur du navigateur. |
ICoreWebView2Controller |
Il existe plusieurs WebViewControllers dans WebView2Browser et nous récupérons les WebViews associées à partir d’eux. |
ICoreWebView2NavigationCompletedEventHandler |
Utilisé avec add_NavigationCompleted pour mettre à jour le bouton Recharger dans l’interface utilisateur du navigateur. |
ICoreWebView2Settings |
Permet de désactiver DevTools dans l’interface utilisateur du navigateur. |
ICoreWebView2SourceChangedEventHandler |
Utilisé avec add_SourceChanged pour mettre à jour la barre d’adresse dans l’interface utilisateur du navigateur. |
ICoreWebView2WebMessageReceivedEventHandler |
C’est l’une des API les plus importantes pour WebView2Browser. La plupart des fonctionnalités impliquant la communication entre WebViews l’utilisent. |
API ICoreWebView2 |
Fonctionnalités |
|---|---|
add_NavigationStarting |
Permet d’afficher le bouton Annuler la navigation dans l’affichage web des contrôles. |
add_SourceChanged |
Permet de mettre à jour la barre d’adresse. |
add_HistoryChanged |
Permet de mettre à jour les boutons Précédent/Suivant. |
add_NavigationCompleted |
Permet d’afficher le bouton de rechargement une fois la navigation terminée. |
ExecuteScript |
Utilisé pour obtenir le titre et le favicon d’une page visitée. |
PostWebMessageAsJson |
Utilisé pour communiquer des WebViews. Tous les messages utilisent JSON pour transmettre les paramètres nécessaires. |
add_WebMessageReceived |
Utilisé pour gérer les messages web publiés sur le WebView. |
CallDevToolsProtocolMethod |
Permet de activer l’écoute des événements de sécurité, qui notifiera des modifications de l’état de status de sécurité dans un document. |
API ICoreWebView2Controller |
Fonctionnalités |
|---|---|
get_CoreWebView2 |
Utilisé pour obtenir le CoreWebView2 associé à ce CoreWebView2Controller. |
add_LostFocus |
Permet de masquer la liste déroulante des options lorsque l’utilisateur clique en dehors de celle-ci. |
Implémentation des fonctionnalités
Les sections ci-dessous décrivent comment certaines des fonctionnalités de WebView2Browser ont été implémentées. Vous pouvez consulter le code source pour plus de détails sur la façon dont tout fonctionne ici. Plan :
Principes de base
Configurer l’environnement, créer un WebView
WebView2 vous permet d’héberger du contenu web dans votre application Windows. Il expose les globaux CreateCoreWebView2Environment et CreateCoreWebView2EnvironmentWithOptions à partir desquels nous pouvons créer les deux environnements distincts pour l’interface utilisateur et le contenu du navigateur.
// Get directory for user data. This will be kept separated from the
// directory for the browser UI data.
std::wstring userDataDirectory = GetAppDataDirectory();
userDataDirectory.append(L"\\User Data");
// Create WebView environment for web content requested by the user. All
// tabs will be created from this environment and kept isolated from the
// browser UI. This environment is created first so the UI can request new
// tabs when it's ready.
HRESULT hr = CreateCoreWebView2EnvironmentWithOptions(nullptr, userDataDirectory.c_str(),
L"", Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
[this](HRESULT result, ICoreWebView2Environment* env) -> HRESULT
{
RETURN_IF_FAILED(result);
m_contentEnv = env;
HRESULT hr = InitUIWebViews();
if (!SUCCEEDED(hr))
{
OutputDebugString(L"UI WebViews environment creation failed\n");
}
return hr;
}).Get());
HRESULT BrowserWindow::InitUIWebViews()
{
// Get data directory for browser UI data
std::wstring browserDataDirectory = GetAppDataDirectory();
browserDataDirectory.append(L"\\Browser Data");
// Create WebView environment for browser UI. A separate data directory is
// used to isolate the browser UI from web content requested by the user.
return CreateCoreWebView2EnvironmentWithOptions(nullptr, browserDataDirectory.c_str(),
L"", Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
[this](HRESULT result, ICoreWebView2Environment* env) -> HRESULT
{
// Environment is ready, create the WebView
m_uiEnv = env;
RETURN_IF_FAILED(CreateBrowserControlsWebView());
RETURN_IF_FAILED(CreateBrowserOptionsWebView());
return S_OK;
}).Get());
}
Nous utilisons ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler pour créer les WebViews de l’interface utilisateur une fois que l’environnement est prêt.
HRESULT BrowserWindow::CreateBrowserControlsWebView()
{
return m_uiEnv->CreateCoreWebView2Controller(m_hWnd, Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
[this](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT
{
if (!SUCCEEDED(result))
{
OutputDebugString(L"Controls WebView creation failed\n");
return result;
}
// WebView created
m_controlsController = controller;
CheckFailure(m_controlsController->get_CoreWebView2(&m_controlsWebView), L"");
wil::com_ptr<ICoreWebView2Settings> settings;
RETURN_IF_FAILED(m_controlsWebView->get_Settings(&settings));
RETURN_IF_FAILED(settings->put_AreDevToolsEnabled(FALSE));
RETURN_IF_FAILED(m_controlsController->add_ZoomFactorChanged(Callback<ICoreWebView2ZoomFactorChangedEventHandler>(
[](ICoreWebView2Controller* controller, IUnknown* args) -> HRESULT
{
controller->put_ZoomFactor(1.0);
return S_OK;
}
).Get(), &m_controlsZoomToken));
RETURN_IF_FAILED(m_controlsWebView->add_WebMessageReceived(m_uiMessageBroker.Get(), &m_controlsUIMessageBrokerToken));
RETURN_IF_FAILED(ResizeUIWebViews());
std::wstring controlsPath = GetFullPathFor(L"wvbrowser_ui\\controls_ui\\default.html");
RETURN_IF_FAILED(m_controlsWebView->Navigate(controlsPath.c_str()));
return S_OK;
}).Get());
}
Nous mettons en place quelques éléments ici. L’interface ICoreWebView2Settings est utilisée pour désactiver DevTools dans la WebView alimentant les contrôles du navigateur. Nous ajoutons également un gestionnaire pour les messages web reçus. Ce gestionnaire nous permettra de faire quelque chose lorsque l’utilisateur interagit avec les contrôles de ce WebView.
Accéder à la page web
Vous pouvez naviguer vers une page web en entrant son URI dans la barre d’adresses. Lorsque vous appuyez sur Entrée, les contrôles WebView publient un message web sur l’application hôte afin que celle-ci puisse naviguer dans l’onglet actif jusqu’à l’emplacement spécifié. Le code ci-dessous montre comment l’application Win32 hôte traitera ce message.
case MG_NAVIGATE:
{
std::wstring uri(args.at(L"uri").as_string());
std::wstring browserScheme(L"browser://");
if (uri.substr(0, browserScheme.size()).compare(browserScheme) == 0)
{
// No encoded search URI
std::wstring path = uri.substr(browserScheme.size());
if (path.compare(L"favorites") == 0 ||
path.compare(L"settings") == 0 ||
path.compare(L"history") == 0)
{
std::wstring filePath(L"wvbrowser_ui\\content_ui\\");
filePath.append(path);
filePath.append(L".html");
std::wstring fullPath = GetFullPathFor(filePath.c_str());
CheckFailure(m_tabs.at(m_activeTabId)->m_contentWebView->Navigate(fullPath.c_str()), L"Can't navigate to browser page.");
}
else
{
OutputDebugString(L"Requested unknown browser page\n");
}
}
else if (!SUCCEEDED(m_tabs.at(m_activeTabId)->m_contentWebView->Navigate(uri.c_str())))
{
CheckFailure(m_tabs.at(m_activeTabId)->m_contentWebView->Navigate(args.at(L"encodedSearchURI").as_string().c_str()), L"Can't navigate to requested page.");
}
}
break;
WebView2Browser vérifie la case activée de l’URI par rapport aux pages du navigateur (favoris, paramètres, historique) et navigue jusqu’à l’emplacement demandé ou utilise l’URI fourni pour rechercher Bing comme solution de secours.
Mise à jour de la barre d’adresse
La barre d’adresses est mise à jour chaque fois qu’il y a un changement dans la source du document de l’onglet actif et avec d’autres contrôles lors du changement d’onglet. Chaque WebView déclenche un événement lorsque l’état du document change, nous pouvons utiliser cet événement pour obtenir la nouvelle source sur les mises à jour et transmettre la modification aux contrôles WebView (nous allons également mettre à jour les boutons Revenir en arrière et Suivant).
// Register event handler for doc state change
RETURN_IF_FAILED(m_contentWebView->add_SourceChanged(Callback<ICoreWebView2SourceChangedEventHandler>(
[this, browserWindow](ICoreWebView2* webview, ICoreWebView2SourceChangedEventArgs* args) -> HRESULT
{
BrowserWindow::CheckFailure(browserWindow->HandleTabURIUpdate(m_tabId, webview), L"Can't update Address bar");
return S_OK;
}).Get(), &m_uriUpdateForwarderToken));
HRESULT BrowserWindow::HandleTabURIUpdate(size_t tabId, ICoreWebView2* webview)
{
wil::unique_cotaskmem_string source;
RETURN_IF_FAILED(webview->get_Source(&source));
web::json::value jsonObj = web::json::value::parse(L"{}");
jsonObj[L"message"] = web::json::value(MG_UPDATE_URI);
jsonObj[L"args"] = web::json::value::parse(L"{}");
jsonObj[L"args"][L"tabId"] = web::json::value::number(tabId);
jsonObj[L"args"][L"uri"] = web::json::value(source.get());
// ...
RETURN_IF_FAILED(PostJsonToWebView(jsonObj, m_controlsWebView.Get()));
return S_OK;
}
HRESULT BrowserWindow::HandleTabHistoryUpdate(size_t tabId, ICoreWebView2* webview)
{
// ...
BOOL canGoForward = FALSE;
RETURN_IF_FAILED(webview->get_CanGoForward(&canGoForward));
jsonObj[L"args"][L"canGoForward"] = web::json::value::boolean(canGoForward);
BOOL canGoBack = FALSE;
RETURN_IF_FAILED(webview->get_CanGoBack(&canGoBack));
jsonObj[L"args"][L"canGoBack"] = web::json::value::boolean(canGoBack);
RETURN_IF_FAILED(PostJsonToWebView(jsonObj, m_controlsWebView.Get()));
return S_OK;
}
Nous avons envoyé le MG_UPDATE_URI message avec l’URI aux contrôles WebView. Nous voulons maintenant répercuter ces modifications sur l’état des onglets et mettre à jour l’interface utilisateur si nécessaire.
case commands.MG_UPDATE_URI:
if (isValidTabId(args.tabId)) {
const tab = tabs.get(args.tabId);
let previousURI = tab.uri;
// Update the tab state
tab.uri = args.uri;
tab.uriToShow = args.uriToShow;
tab.canGoBack = args.canGoBack;
tab.canGoForward = args.canGoForward;
// If the tab is active, update the controls UI
if (args.tabId == activeTabId) {
updateNavigationUI(message);
}
// ...
}
break;
Revenir en arrière, aller de l’avant
Chaque WebView conservera un historique des navigations qu’il a effectuées, nous n’avons donc besoin que de connecter l’interface utilisateur du navigateur avec les méthodes correspondantes. Si vous naviguez vers l’arrière ou l’avant dans l’affichage web de l’onglet actif, les boutons publient un message web sur l’application hôte lorsque vous cliquez dessus.
Le côté JavaScript :
document.querySelector('#btn-forward').addEventListener('click', function(e) {
if (document.getElementById('btn-forward').className === 'btn') {
var message = {
message: commands.MG_GO_FORWARD,
args: {}
};
window.chrome.webview.postMessage(message);
}
});
document.querySelector('#btn-back').addEventListener('click', function(e) {
if (document.getElementById('btn-back').className === 'btn') {
var message = {
message: commands.MG_GO_BACK,
args: {}
};
window.chrome.webview.postMessage(message);
}
});
Côté application hôte :
case MG_GO_FORWARD:
{
CheckFailure(m_tabs.at(m_activeTabId)->m_contentWebView->GoForward(), L"");
}
break;
case MG_GO_BACK:
{
CheckFailure(m_tabs.at(m_activeTabId)->m_contentWebView->GoBack(), L"");
}
break;
Rechargement, arrêter la navigation
Nous utilisons l’événement NavigationStarting déclenché par un WebView de contenu pour mettre à jour l’état de chargement d’onglet associé dans le WebView de contrôles. De même, lorsqu’un WebView déclenche l’événement NavigationCompleted , nous utilisons cet événement pour demander aux contrôles WebView de mettre à jour l’état de la tabulation. L’état actif de l’onglet dans l’affichage web des contrôles détermine s’il faut afficher le bouton de rechargement ou le bouton annuler. Chacun d’entre eux publie un message dans l’application hôte lorsqu’on clique dessus, afin que l’affichage web de cet onglet puisse être rechargé ou que sa navigation soit annulée, en conséquence.
function reloadActiveTabContent() {
var message = {
message: commands.MG_RELOAD,
args: {}
};
window.chrome.webview.postMessage(message);
}
// ...
document.querySelector('#btn-reload').addEventListener('click', function(e) {
var btnReload = document.getElementById('btn-reload');
if (btnReload.className === 'btn-cancel') {
var message = {
message: commands.MG_CANCEL,
args: {}
};
window.chrome.webview.postMessage(message);
} else if (btnReload.className === 'btn') {
reloadActiveTabContent();
}
});
case MG_RELOAD:
{
CheckFailure(m_tabs.at(m_activeTabId)->m_contentWebView->Reload(), L"");
}
break;
case MG_CANCEL:
{
CheckFailure(m_tabs.at(m_activeTabId)->m_contentWebView->CallDevToolsProtocolMethod(L"Page.stopLoading", L"{}", nullptr), L"");
}
Quelques fonctionnalités intéressantes
Communication des WebViews
Nous devons communiquer les WebViews qui alimentent les onglets et l’interface utilisateur, afin que les interactions utilisateur dans la WebView d’un onglet aient l’effet souhaité dans l’autre WebView. WebView2Browser utilise un ensemble d’API WebView2 très utiles à cette fin, notamment PostWebMessageAsJson, add_WebMessageReceived et ICoreWebView2WebMessageReceivedEventHandler.
Du côté JavaScript, nous utilisons l’objet window.chrome.webview exposé pour appeler la méthode et ajouter un lister d’événements pour les postMessage messages reçus.
HRESULT BrowserWindow::CreateBrowserControlsWebView()
{
return m_uiEnv->CreateCoreWebView2Controller(m_hWnd, Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
[this](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT
{
// ...
RETURN_IF_FAILED(m_controlsWebView->add_WebMessageReceived(m_uiMessageBroker.Get(), &m_controlsUIMessageBrokerToken));
// ...
return S_OK;
}).Get());
}
HRESULT BrowserWindow::PostJsonToWebView(web::json::value jsonObj, ICoreWebView2* webview)
{
utility::stringstream_t stream;
jsonObj.serialize(stream);
return webview->PostWebMessageAsJson(stream.str().c_str());
}
// ...
HRESULT BrowserWindow::HandleTabNavStarting(size_t tabId, ICoreWebView2* webview)
{
web::json::value jsonObj = web::json::value::parse(L"{}");
jsonObj[L"message"] = web::json::value(MG_NAV_STARTING);
jsonObj[L"args"] = web::json::value::parse(L"{}");
jsonObj[L"args"][L"tabId"] = web::json::value::number(tabId);
return PostJsonToWebView(jsonObj, m_controlsWebView.Get());
}
function init() {
window.chrome.webview.addEventListener('message', messageHandler);
refreshControls();
refreshTabs();
createNewTab(true);
}
// ...
function reloadActiveTabContent() {
var message = {
message: commands.MG_RELOAD,
args: {}
};
window.chrome.webview.postMessage(message);
}
Gestion des onglets
Un nouvel onglet est créé chaque fois que l’utilisateur clique sur le bouton nouvel onglet à droite des onglets ouverts. L’affichage web du contrôle publie un message sur l’application hôte pour créer l’affichage web de cet onglet et créer un objet qui suit son état.
function createNewTab(shouldBeActive) {
const tabId = getNewTabId();
var message = {
message: commands.MG_CREATE_TAB,
args: {
tabId: parseInt(tabId),
active: shouldBeActive || false
}
};
window.chrome.webview.postMessage(message);
tabs.set(parseInt(tabId), {
title: 'New Tab',
uri: '',
uriToShow: '',
favicon: 'img/favicon.png',
isFavorite: false,
isLoading: false,
canGoBack: false,
canGoForward: false,
securityState: 'unknown',
historyItemId: INVALID_HISTORY_ID
});
loadTabUI(tabId);
if (shouldBeActive) {
switchToTab(tabId, false);
}
}
Du côté de l’application hôte, le ICoreWebView2WebMessageReceivedEventHandler inscrit intercepte le message et crée l’affichage web pour cet onglet.
case MG_CREATE_TAB:
{
size_t id = args.at(L"tabId").as_number().to_uint32();
bool shouldBeActive = args.at(L"active").as_bool();
std::unique_ptr<Tab> newTab = Tab::CreateNewTab(m_hWnd, m_contentEnv.Get(), id, shouldBeActive);
std::map<size_t, std::unique_ptr<Tab>>::iterator it = m_tabs.find(id);
if (it == m_tabs.end())
{
m_tabs.insert(std::pair<size_t,std::unique_ptr<Tab>>(id, std::move(newTab)));
}
else
{
m_tabs.at(id)->m_contentWebView->Close();
it->second = std::move(newTab);
}
}
break;
std::unique_ptr<Tab> Tab::CreateNewTab(HWND hWnd, ICoreWebView2Environment* env, size_t id, bool shouldBeActive)
{
std::unique_ptr<Tab> tab = std::make_unique<Tab>();
tab->m_parentHWnd = hWnd;
tab->m_tabId = id;
tab->SetMessageBroker();
tab->Init(env, shouldBeActive);
return tab;
}
HRESULT Tab::Init(ICoreWebView2Environment* env, bool shouldBeActive)
{
return env->CreateCoreWebView2Controller(m_parentHWnd, Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
[this, shouldBeActive](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT {
if (!SUCCEEDED(result))
{
OutputDebugString(L"Tab WebView creation failed\n");
return result;
}
m_contentController = controller;
BrowserWindow::CheckFailure(m_contentController->get_CoreWebView2(&m_contentWebView), L"");
BrowserWindow* browserWindow = reinterpret_cast<BrowserWindow*>(GetWindowLongPtr(m_parentHWnd, GWLP_USERDATA));
RETURN_IF_FAILED(m_contentWebView->add_WebMessageReceived(m_messageBroker.Get(), &m_messageBrokerToken));
// Register event handler for history change
RETURN_IF_FAILED(m_contentWebView->add_HistoryChanged(Callback<ICoreWebView2HistoryChangedEventHandler>(
[this, browserWindow](ICoreWebView2* webview, IUnknown* args) -> HRESULT
{
BrowserWindow::CheckFailure(browserWindow->HandleTabHistoryUpdate(m_tabId, webview), L"Can't update go back/forward buttons.");
return S_OK;
}).Get(), &m_historyUpdateForwarderToken));
// Register event handler for source change
RETURN_IF_FAILED(m_contentWebView->add_SourceChanged(Callback<ICoreWebView2SourceChangedEventHandler>(
[this, browserWindow](ICoreWebView2* webview, ICoreWebView2SourceChangedEventArgs* args) -> HRESULT
{
BrowserWindow::CheckFailure(browserWindow->HandleTabURIUpdate(m_tabId, webview), L"Can't update Address bar");
return S_OK;
}).Get(), &m_uriUpdateForwarderToken));
RETURN_IF_FAILED(m_contentWebView->add_NavigationStarting(Callback<ICoreWebView2NavigationStartingEventHandler>(
[this, browserWindow](ICoreWebView2* webview, ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT
{
BrowserWindow::CheckFailure(browserWindow->HandleTabNavStarting(m_tabId, webview), L"Can't update reload button");
return S_OK;
}).Get(), &m_navStartingToken));
RETURN_IF_FAILED(m_contentWebView->add_NavigationCompleted(Callback<ICoreWebView2NavigationCompletedEventHandler>(
[this, browserWindow](ICoreWebView2* webview, ICoreWebView2NavigationCompletedEventArgs* args) -> HRESULT
{
BrowserWindow::CheckFailure(browserWindow->HandleTabNavCompleted(m_tabId, webview, args), L"Can't update reload button");
return S_OK;
}).Get(), &m_navCompletedToken));
// Handle security state updates
RETURN_IF_FAILED(m_contentWebView->Navigate(L"https://www.bing.com"));
browserWindow->HandleTabCreated(m_tabId, shouldBeActive);
return S_OK;
}).Get());
}
L’onglet enregistre tous les gestionnaires afin de pouvoir transférer les mises à jour vers l’affichage web des contrôles lorsque les événements se déclenchent. L’onglet est prêt et s’affiche dans la zone de contenu du navigateur. Un clic sur un onglet dans l’affichage web des contrôles affiche un message dans l’application hôte, qui à son tour masque l’affichage web de l’onglet précédemment actif et affiche celui de l’onglet cliqué.
HRESULT BrowserWindow::SwitchToTab(size_t tabId)
{
size_t previousActiveTab = m_activeTabId;
RETURN_IF_FAILED(m_tabs.at(tabId)->ResizeWebView());
RETURN_IF_FAILED(m_tabs.at(tabId)->m_contentWebView->put_IsVisible(TRUE));
m_activeTabId = tabId;
if (previousActiveTab != INVALID_TAB_ID && previousActiveTab != m_activeTabId)
{
RETURN_IF_FAILED(m_tabs.at(previousActiveTab)->m_contentWebView->put_IsVisible(FALSE));
}
return S_OK;
}
Mise à jour de l’icône de sécurité
Nous utilisons la méthode CallDevToolsProtocolMethod pour activer l’écoute des événements de sécurité. Chaque fois qu’un securityStateChanged événement est déclenché, nous utilisons le nouvel état pour mettre à jour l’icône de sécurité sur l’affichage web des contrôles.
// Enable listening for security events to update secure icon
RETURN_IF_FAILED(m_contentWebView->CallDevToolsProtocolMethod(L"Security.enable", L"{}", nullptr));
BrowserWindow::CheckFailure(m_contentWebView->GetDevToolsProtocolEventReceiver(L"Security.securityStateChanged", &m_securityStateChangedReceiver), L"");
// Forward security status updates to browser
RETURN_IF_FAILED(m_securityStateChangedReceiver->add_DevToolsProtocolEventReceived(Callback<ICoreWebView2DevToolsProtocolEventReceivedEventHandler>(
[this, browserWindow](ICoreWebView2* webview, ICoreWebView2DevToolsProtocolEventReceivedEventArgs* args) -> HRESULT
{
BrowserWindow::CheckFailure(browserWindow->HandleTabSecurityUpdate(m_tabId, webview, args), L"Can't update security icon");
return S_OK;
}).Get(), &m_securityUpdateToken));
HRESULT BrowserWindow::HandleTabSecurityUpdate(size_t tabId, ICoreWebView2* webview, ICoreWebView2DevToolsProtocolEventReceivedEventArgs* args)
{
wil::unique_cotaskmem_string jsonArgs;
RETURN_IF_FAILED(args->get_ParameterObjectAsJson(&jsonArgs));
web::json::value securityEvent = web::json::value::parse(jsonArgs.get());
web::json::value jsonObj = web::json::value::parse(L"{}");
jsonObj[L"message"] = web::json::value(MG_SECURITY_UPDATE);
jsonObj[L"args"] = web::json::value::parse(L"{}");
jsonObj[L"args"][L"tabId"] = web::json::value::number(tabId);
jsonObj[L"args"][L"state"] = securityEvent.at(L"securityState");
return PostJsonToWebView(jsonObj, m_controlsWebView.Get());
}
case commands.MG_SECURITY_UPDATE:
if (isValidTabId(args.tabId)) {
const tab = tabs.get(args.tabId);
tab.securityState = args.state;
if (args.tabId == activeTabId) {
updateNavigationUI(message);
}
}
break;
Remplissage de l’historique
WebView2Browser utilise IndexedDB dans les contrôles WebView pour stocker des éléments d’historique, juste un exemple de la façon dont WebView2 vous permet d’accéder aux technologies Web standard comme vous le feriez dans le navigateur. L’élément de navigation sera créé dès que l’URI sera mis à jour. Ces éléments sont ensuite récupérés par l’interface utilisateur de l’historique dans un onglet utilisant .window.chrome.postMessage
Dans ce cas, la plupart des fonctionnalités sont implémentées à l’aide de JavaScript aux deux extrémités (contrôle WebView et contenu WebView chargement de l’interface utilisateur), de sorte que l’application hôte agit uniquement en tant que courtier de messages pour communiquer ces fins.
case commands.MG_UPDATE_URI:
if (isValidTabId(args.tabId)) {
// ...
// Don't add history entry if URI has not changed
if (tab.uri == previousURI) {
break;
}
// Filter URIs that should not appear in history
if (!tab.uri || tab.uri == 'about:blank') {
tab.historyItemId = INVALID_HISTORY_ID;
break;
}
if (tab.uriToShow && tab.uriToShow.substring(0, 10) == 'browser://') {
tab.historyItemId = INVALID_HISTORY_ID;
break;
}
addHistoryItem(historyItemFromTab(args.tabId), (id) => {
tab.historyItemId = id;
});
}
break;
function addHistoryItem(item, callback) {
queryDB((db) => {
let transaction = db.transaction(['history'], 'readwrite');
let historyStore = transaction.objectStore('history');
// Check if an item for this URI exists on this day
let currentDate = new Date();
let year = currentDate.getFullYear();
let month = currentDate.getMonth();
let date = currentDate.getDate();
let todayDate = new Date(year, month, date);
let existingItemsIndex = historyStore.index('stampedURI');
let lowerBound = [item.uri, todayDate];
let upperBound = [item.uri, currentDate];
let range = IDBKeyRange.bound(lowerBound, upperBound);
let request = existingItemsIndex.openCursor(range);
request.onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
// There's an entry for this URI, update the item
cursor.value.timestamp = item.timestamp;
let updateRequest = cursor.update(cursor.value);
updateRequest.onsuccess = function(event) {
if (callback) {
callback(event.target.result.primaryKey);
}
};
} else {
// No entry for this URI, add item
let addItemRequest = historyStore.add(item);
addItemRequest.onsuccess = function(event) {
if (callback) {
callback(event.target.result);
}
};
}
};
});
}
Gestion des JSON et des URI
WebView2Browser utilise le cpprestsdk de Microsoft (Casablanca) pour gérer tous les JSON du côté C++ des choses. IUri et CreateUri sont également utilisés pour analyser les chemins d’accès aux fichiers en URI et peuvent également être utilisés pour d’autres URI.