diff --git a/modules/juce_gui_extra/juce_gui_extra.cpp b/modules/juce_gui_extra/juce_gui_extra.cpp index 4b63a12b80..6ade70d51a 100644 --- a/modules/juce_gui_extra/juce_gui_extra.cpp +++ b/modules/juce_gui_extra/juce_gui_extra.cpp @@ -149,6 +149,7 @@ namespace juce #if JUCE_WEB_BROWSER bool WebBrowserComponent::pageAboutToLoad (const String&) { return true; } void WebBrowserComponent::pageFinishedLoading (const String&) {} + bool WebBrowserComponent::pageLoadHadNetworkError (const String&) { return true; } void WebBrowserComponent::windowCloseRequest() {} void WebBrowserComponent::newWindowAttemptingToLoad (const String&) {} #endif diff --git a/modules/juce_gui_extra/misc/juce_WebBrowserComponent.h b/modules/juce_gui_extra/misc/juce_WebBrowserComponent.h index c4b1edc4a5..e3b9d4c4eb 100644 --- a/modules/juce_gui_extra/misc/juce_WebBrowserComponent.h +++ b/modules/juce_gui_extra/misc/juce_WebBrowserComponent.h @@ -95,6 +95,18 @@ public: /** This callback happens when the browser has finished loading a page. */ virtual void pageFinishedLoading (const String& url); + /** This callback happens when a network error was encountered while + trying to load a page. + + You can override this method to show some other error page by calling + goToURL. Return true to allow the browser to carry on to the internal + browser error page. + + The errorInfo contains some platform dependent string describing the + error. + */ + virtual bool pageLoadHadNetworkError (const String& errorInfo); + /** This callback occurs when a script or other activity in the browser asks for the window to be closed. */ diff --git a/modules/juce_gui_extra/native/juce_linux_X11_WebBrowserComponent.cpp b/modules/juce_gui_extra/native/juce_linux_X11_WebBrowserComponent.cpp index eb08abeed4..7aacb50d12 100644 --- a/modules/juce_gui_extra/native/juce_linux_X11_WebBrowserComponent.cpp +++ b/modules/juce_gui_extra/native/juce_linux_X11_WebBrowserComponent.cpp @@ -180,6 +180,9 @@ public: g_signal_connect (webview, "load-changed", G_CALLBACK (loadChangedCallback), this); + g_signal_connect (webview, "load-failed", + G_CALLBACK (loadFailedCallback), this); + gtk_widget_show_all (plug); unsigned long wID = (unsigned long) gtk_plug_get_id (GTK_PLUG (plug)); @@ -350,6 +353,14 @@ public: return false; } + void onLoadFailed (GError* error) + { + DynamicObject::Ptr params = new DynamicObject; + + params->setProperty ("error", String (error != nullptr ? error->message : "unknown error")); + CommandReceiver::sendCommand (outChannel, "pageLoadHadNetworkError", var (params)); + } + private: static gboolean pipeReadyStatic (gint fd, GIOCondition condition, gpointer user) { @@ -373,6 +384,16 @@ private: owner.onLoadChanged (loadEvent); } + static void loadFailedCallback (WebKitWebView*, + WebKitLoadEvent /*loadEvent*/, + gchar* /*failing_uri*/, + GError* error, + gpointer user) + { + GtkChildProcess& owner = *reinterpret_cast (user); + owner.onLoadFailed (error); + } + int outChannel; CommandReceiver receiver; WebKitWebView* webview = nullptr; @@ -566,6 +587,7 @@ private: else if (cmd == "pageFinishedLoading") owner.pageFinishedLoading (url); else if (cmd == "windowCloseRequest") owner.windowCloseRequest(); else if (cmd == "newWindowAttemptingToLoad") owner.newWindowAttemptingToLoad (url); + else if (cmd == "pageLoadHadNetworkError") handlePageLoadHadNetworkError (params); threadBlocker.signal(); } @@ -585,6 +607,14 @@ private: } } + void handlePageLoadHadNetworkError (const var& params) + { + String error = params.getProperty ("error", "Unknown error"); + + if (owner.pageLoadHadNetworkError (error)) + goToURL (String ("data:text/plain,") + error, nullptr, nullptr); + } + void handleCommand (const String& cmd, const var& params) override { threadBlocker.reset(); diff --git a/modules/juce_gui_extra/native/juce_mac_WebBrowserComponent.mm b/modules/juce_gui_extra/native/juce_mac_WebBrowserComponent.mm index 6f71c3ac0e..154d322338 100644 --- a/modules/juce_gui_extra/native/juce_mac_WebBrowserComponent.mm +++ b/modules/juce_gui_extra/native/juce_mac_WebBrowserComponent.mm @@ -35,6 +35,8 @@ struct DownloadClickDetectorClass : public ObjCClass addMethod (@selector (webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener:), decidePolicyForNewWindowAction, "v@:@@@@@"); addMethod (@selector (webView:didFinishLoadForFrame:), didFinishLoadForFrame, "v@:@@"); + addMethod (@selector (webView:didFailLoadWithError:forFrame:), didFailLoadWithError, "v@:@@@"); + addMethod (@selector (webView:didFailProvisionalLoadWithError:forFrame:), didFailLoadWithError, "v@:@@@"); addMethod (@selector (webView:willCloseFrame:), willCloseFrame, "v@:@@"); addMethod (@selector (webView:runOpenPanelForFileButtonWithResultListener:allowMultipleFiles:), runOpenPanel, "v@:@@", @encode (BOOL)); @@ -78,6 +80,20 @@ private: } } + static void didFailLoadWithError (id self, SEL, WebView* sender, NSError* error, WebFrame* frame) + { + if ([frame isEqual: [sender mainFrame]]) + { + const char* errorString = [[error localizedDescription] UTF8String]; + + bool proceedToErrorPage = getOwner (self)->pageLoadHadNetworkError (errorString); + + // WebKit doesn't have an internal error page, so make a really simple one ourselves + if (proceedToErrorPage) + getOwner(self)->goToURL (String ("data:text/plain,") + errorString); + } + } + static void willCloseFrame (id self, SEL, WebView*, WebFrame*) { getOwner (self)->windowCloseRequest(); diff --git a/modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp b/modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp index 41c5d0bd03..585ff92c38 100644 --- a/modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp +++ b/modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp @@ -173,6 +173,29 @@ private: return S_OK; } + if (dispIdMember == DISPID_NAVIGATEERROR) + { + int statusCode = pDispParams->rgvarg[1].pvarVal->intVal; + *pDispParams->rgvarg[0].pboolVal = VARIANT_FALSE; + + // IWebBrowser2 also reports http status codes here, we need + // report only network erros + if (statusCode < 0) + { + LPTSTR messageBuffer = nullptr; + size_t size = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, statusCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &messageBuffer, 0, NULL); + + String message(messageBuffer, size); + LocalFree(messageBuffer); + + if (!owner.pageLoadHadNetworkError(message)) + *pDispParams->rgvarg[0].pboolVal = VARIANT_TRUE; + } + + return S_OK; + } + if (dispIdMember == 263 /*DISPID_WINDOWCLOSING*/) { owner.windowCloseRequest();