diff --git a/include/cef.h b/include/cef.h index 6c8b35648..b2dbc16cc 100644 --- a/include/cef.h +++ b/include/cef.h @@ -242,6 +242,50 @@ protected: }; +// Class representing a rectangle. +class CefRect : public cef_rect_t +{ +public: + CefRect() + { + x = y = width = height = 0; + } + CefRect(int x, int y, int width, int height) + { + set(x, y, width, height); + } + + CefRect(const cef_rect_t& r) + { + set(r.x, r.y, r.width, r.height); + } + CefRect& operator=(const cef_rect_t& r) + { + x = r.x; + y = r.y; + width = r.width; + height = r.height; + return *this; + } + + bool isEmpty() const { return width <= 0 || height <= 0; } + void set(int x, int y, int width, int height) + { + this->x = x, this->y = y, this->width = width, this->height = height; + } +}; + +inline bool operator==(const CefRect& a, const CefRect& b) +{ + return a.x == b.x && a.y == b.y && a.width == b.width && a.height == b.height; +} + +inline bool operator!=(const CefRect& a, const CefRect& b) +{ + return !(a == b); +} + + // Class used to represent a browser window. All methods exposed by this class // should be thread safe. /*--cef(source=library)--*/ @@ -319,6 +363,19 @@ public: // Returns the names of all existing frames. /*--cef()--*/ virtual void GetFrameNames(std::vector& names) =0; + + // Search for |searchText|. |identifier| can be used to have multiple searches + // running simultaniously. |forward| indicates whether to search forward or + // backward within the page. |matchCase| indicates whether the search should + // be case-sensitive. |findNext| indicates whether this is the first request + // or a follow-up. + /*--cef()--*/ + virtual void Find(int identifier, const std::wstring& searchText, + bool forward, bool matchCase, bool findNext) =0; + + // Cancel all searches that are currently going on. + /*--cef()--*/ + virtual void StopFinding(bool clearSelection) =0; }; @@ -637,6 +694,18 @@ public: virtual RetVal HandleConsoleMessage(CefRefPtr browser, const std::wstring& message, const std::wstring& source, int line) =0; + + // Called to report find results returned by CefBrowser::Find(). |identifer| + // is the identifier passed to CefBrowser::Find(), |count| is the number of + // matches currently identified, |selectionRect| is the location of where the + // match was found (in window coordinates), |activeMatchOrdinal| is the + // current position in the search results, and |finalUpdate| is true if this + // is the last find notification. The return value is currently ignored. + /*--cef()--*/ + virtual RetVal HandleFindResult(CefRefPtr browser, + int identifier, int count, + const CefRect& selectionRect, + int activeMatchOrdinal, bool finalUpdate) =0; }; diff --git a/include/cef_capi.h b/include/cef_capi.h index 3bca6fc95..5deb9ba6f 100644 --- a/include/cef_capi.h +++ b/include/cef_capi.h @@ -213,6 +213,18 @@ typedef struct _cef_browser_t void (CEF_CALLBACK *get_frame_names)(struct _cef_browser_t* self, cef_string_list_t names); + // Search for |searchText|. |identifier| can be used to have multiple searches + // running simultaniously. |forward| indicates whether to search forward or + // backward within the page. |matchCase| indicates whether the search should + // be case-sensitive. |findNext| indicates whether this is the first request + // or a follow-up. + void (CEF_CALLBACK *find)(struct _cef_browser_t* self, int identifier, + const wchar_t* searchText, int forward, int matchCase, int findNext); + + // Cancel all searches that are currently going on. + void (CEF_CALLBACK *stop_finding)(struct _cef_browser_t* self, + int clearSelection); + } cef_browser_t; @@ -495,6 +507,18 @@ typedef struct _cef_handler_t struct _cef_handler_t* self, struct _cef_browser_t* browser, const wchar_t* message, const wchar_t* source, int line); + // Called to report find results returned by cef_browser_t::find(). + // |identifer| is the identifier passed to cef_browser_t::find(), |count| is + // the number of matches currently identified, |selectionRect| is the location + // of where the match was found (in window coordinates), |activeMatchOrdinal| + // is the current position in the search results, and |finalUpdate| is true + // (1) if this is the last find notification. The return value is currently + // ignored. + enum cef_retval_t (CEF_CALLBACK *handle_find_result)( + struct _cef_handler_t* self, struct _cef_browser_t* browser, + int identifier, int count, const cef_rect_t* selectionRect, + int activeMatchOrdinal, int finalUpdate); + } cef_handler_t; diff --git a/include/cef_types.h b/include/cef_types.h index 7273cac99..f7ebf9cf5 100644 --- a/include/cef_types.h +++ b/include/cef_types.h @@ -220,6 +220,15 @@ enum cef_handler_keyevent_modifiers_t KEY_META = 1 << 3 }; +// Structure representing a rectangle. +typedef struct _cef_rect_t +{ + int x; + int y; + int width; + int height; +} cef_rect_t; + #ifdef __cplusplus } #endif diff --git a/libcef/browser_impl.cc b/libcef/browser_impl.cc index 4355d2a6d..8ecb9aa3d 100644 --- a/libcef/browser_impl.cc +++ b/libcef/browser_impl.cc @@ -9,17 +9,29 @@ #include "request_impl.h" #include "base/utf_string_conversions.h" +#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h" #include "third_party/WebKit/WebKit/chromium/public/WebFrame.h" #include "third_party/WebKit/WebKit/chromium/public/WebHTTPBody.h" +#include "third_party/WebKit/WebKit/chromium/public/WebPlugin.h" +#include "third_party/WebKit/WebKit/chromium/public/WebPluginDocument.h" +#include "third_party/WebKit/WebKit/chromium/public/WebRange.h" +#include "third_party/WebKit/WebKit/chromium/public/WebRect.h" #include "third_party/WebKit/WebKit/chromium/public/WebScriptSource.h" #include "third_party/WebKit/WebKit/chromium/public/WebString.h" #include "third_party/WebKit/WebKit/chromium/public/WebURL.h" #include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h" #include "third_party/WebKit/WebKit/chromium/public/WebView.h" #include "webkit/glue/glue_serialize.h" +#include "webkit/glue/plugins/webplugin_delegate.h" +#include "webkit/glue/plugins/webplugin_impl.h" +using WebKit::WebDocument; using WebKit::WebFrame; using WebKit::WebHTTPBody; +using WebKit::WebPlugin; +using WebKit::WebPluginDocument; +using WebKit::WebRange; +using WebKit::WebRect; using WebKit::WebScriptSource; using WebKit::WebString; using WebKit::WebURL; @@ -121,6 +133,26 @@ void CefBrowserImpl::GetFrameNames(std::vector& names) } while (it != main_frame); } +void CefBrowserImpl::Find(int identifier, const std::wstring& searchText, + bool forward, bool matchCase, bool findNext) +{ + WebKit::WebFindOptions options; + options.forward = forward; + options.matchCase = matchCase; + options.findNext = findNext; + + // Execute the request on the UI thread. + PostTask(FROM_HERE, NewRunnableMethod(this, + &CefBrowserImpl::UIT_Find, identifier, searchText, options)); +} + +void CefBrowserImpl::StopFinding(bool clearSelection) +{ + // Execute the request on the UI thread. + PostTask(FROM_HERE, NewRunnableMethod(this, + &CefBrowserImpl::UIT_StopFinding, clearSelection)); +} + CefRefPtr CefBrowserImpl::GetCefFrame(WebFrame* frame) { CefRefPtr cef_frame; @@ -676,6 +708,169 @@ void CefBrowserImpl::UIT_HandleAction(CefHandler::MenuId menuId, frame->Release(); } +void CefBrowserImpl::UIT_Find(int identifier, const std::wstring& search_text, + const WebKit::WebFindOptions& options) +{ + WebFrame* main_frame = GetWebView()->mainFrame(); + + if (main_frame->document().isPluginDocument()) { + WebPlugin* plugin = main_frame->document().to().plugin(); + webkit_glue::WebPluginDelegate* delegate = + static_cast(plugin)->delegate(); + if (options.findNext) { + // Just navigate back/forward. + delegate->SelectFindResult(options.forward); + } else { + if (delegate->SupportsFind()) { + delegate->StartFind(UTF16ToUTF8(search_text), + options.matchCase, + identifier); + } else { + // No find results. + UIT_NotifyFindStatus(identifier, 0, gfx::Rect(), 0, true); + } + } + return; + } + + WebFrame* frame_after_main = main_frame->traverseNext(true); + WebFrame* focused_frame = GetWebView()->focusedFrame(); + WebFrame* search_frame = focused_frame; // start searching focused frame. + + bool multi_frame = (frame_after_main != main_frame); + + // If we have multiple frames, we don't want to wrap the search within the + // frame, so we check here if we only have main_frame in the chain. + bool wrap_within_frame = !multi_frame; + + WebRect selection_rect; + bool result = false; + + // If something is selected when we start searching it means we cannot just + // increment the current match ordinal; we need to re-generate it. + WebRange current_selection = focused_frame->selectionRange(); + + do { + result = search_frame->find( + identifier, search_text, options, wrap_within_frame, &selection_rect); + + if (!result) { + // don't leave text selected as you move to the next frame. + search_frame->executeCommand(WebString::fromUTF8("Unselect")); + + // Find the next frame, but skip the invisible ones. + do { + // What is the next frame to search? (we might be going backwards). Note + // that we specify wrap=true so that search_frame never becomes NULL. + search_frame = options.forward ? + search_frame->traverseNext(true) : + search_frame->traversePrevious(true); + } while (!search_frame->hasVisibleContent() && + search_frame != focused_frame); + + // Make sure selection doesn't affect the search operation in new frame. + search_frame->executeCommand(WebString::fromUTF8("Unselect")); + + // If we have multiple frames and we have wrapped back around to the + // focused frame, we need to search it once more allowing wrap within + // the frame, otherwise it will report 'no match' if the focused frame has + // reported matches, but no frames after the focused_frame contain a + // match for the search word(s). + if (multi_frame && search_frame == focused_frame) { + result = search_frame->find( + identifier, search_text, options, true, // Force wrapping. + &selection_rect); + } + } + + GetWebView()->setFocusedFrame(search_frame); + } while (!result && search_frame != focused_frame); + + if (options.findNext && current_selection.isNull()) { + // Force the main_frame to report the actual count. + main_frame->increaseMatchCount(0, identifier); + } else { + // If nothing is found, set result to "0 of 0", otherwise, set it to + // "-1 of 1" to indicate that we found at least one item, but we don't know + // yet what is active. + int ordinal = result ? -1 : 0; // -1 here means, we might know more later. + int match_count = result ? 1 : 0; // 1 here means possibly more coming. + + // If we find no matches then this will be our last status update. + // Otherwise the scoping effort will send more results. + bool final_status_update = !result; + + // Send the search result. + UIT_NotifyFindStatus(identifier, match_count, selection_rect, ordinal, + final_status_update); + + // Scoping effort begins, starting with the mainframe. + search_frame = main_frame; + + main_frame->resetMatchCount(); + + do { + // Cancel all old scoping requests before starting a new one. + search_frame->cancelPendingScopingEffort(); + + // We don't start another scoping effort unless at least one match has + // been found. + if (result) { + // Start new scoping request. If the scoping function determines that it + // needs to scope, it will defer until later. + search_frame->scopeStringMatches(identifier, + search_text, + options, + true); // reset the tickmarks + } + + // Iterate to the next frame. The frame will not necessarily scope, for + // example if it is not visible. + search_frame = search_frame->traverseNext(true); + } while (search_frame != main_frame); + } +} + +void CefBrowserImpl::UIT_StopFinding(bool clear_selection) +{ + WebView* view = GetWebView(); + if (!view) + return; + + WebDocument doc = view->mainFrame()->document(); + if (doc.isPluginDocument()) { + WebPlugin* plugin = view->mainFrame()->document(). + to().plugin(); + webkit_glue::WebPluginDelegate* delegate = + static_cast(plugin)->delegate(); + delegate->StopFind(); + return; + } + + if (clear_selection) + view->focusedFrame()->executeCommand(WebString::fromUTF8("Unselect")); + + WebFrame* frame = view->mainFrame(); + while (frame) { + frame->stopFinding(clear_selection); + frame = frame->traverseNext(false); + } +} + +void CefBrowserImpl::UIT_NotifyFindStatus(int identifier, int count, + const WebKit::WebRect& selection_rect, + int active_match_ordinal, + bool final_update) +{ + if(handler_.get()) + { + CefRect rect(selection_rect.x, selection_rect.y, selection_rect.width, + selection_rect.height); + handler_->HandleFindResult(this, identifier, count, rect, + active_match_ordinal, final_update); + } +} + // CefFrameImpl diff --git a/libcef/browser_impl.h b/libcef/browser_impl.h index 31ff854a2..19455b86d 100644 --- a/libcef/browser_impl.h +++ b/libcef/browser_impl.h @@ -16,6 +16,8 @@ #include "printing/win_printing_context.h" #endif +#include "third_party/WebKit/WebKit/chromium/public/WebFindOptions.h" + namespace WebKit { class WebView; } @@ -52,7 +54,10 @@ public: virtual CefRefPtr GetFocusedFrame(); virtual CefRefPtr GetFrame(const std::wstring& name); virtual void GetFrameNames(std::vector& names); - + virtual void Find(int identifier, const std::wstring& searchText, + bool forward, bool matchCase, bool findNext); + virtual void StopFinding(bool clearSelection); + // CefFrames are light-weight objects managed by the browser and loosely // coupled to a WebFrame object by name. If a CefFrame object does not // already exist for the specified WebFrame one will be created. There is no @@ -91,7 +96,7 @@ public: const std::wstring& jsCode, const std::wstring& scriptUrl, int startLine); - virtual std::wstring GetURL(CefRefPtr frame); + std::wstring GetURL(CefRefPtr frame); WebKit::WebView* GetWebView() const { return webviewhost_.get() ? webviewhost_->webview() : NULL; @@ -212,6 +217,13 @@ public: void UIT_SetUniqueID(int id) { unique_id_ = id; } int UIT_GetUniqueID() { return unique_id_; } + void UIT_Find(int identifier, const std::wstring& search_text, + const WebKit::WebFindOptions& options); + void UIT_StopFinding(bool clear_selection); + void UIT_NotifyFindStatus(int identifier, int count, + const WebKit::WebRect& selection_rect, + int active_match_ordinal, bool final_update); + static bool ImplementsThreadSafeReferenceCounting() { return true; } protected: diff --git a/libcef/browser_webkit_glue.cc b/libcef/browser_webkit_glue.cc index 3e496cc9f..2b0c2e27c 100644 --- a/libcef/browser_webkit_glue.cc +++ b/libcef/browser_webkit_glue.cc @@ -54,93 +54,14 @@ base::StringPiece GetRawDataResource(HMODULE module, int resource_id) { } base::StringPiece NetResourceProvider(int key) { - return GetRawDataResource(::GetModuleHandle(NULL), key); + HMODULE hModule = ::GetModuleHandle(L"libcef.dll"); + if(!hModule) + hModule = ::GetModuleHandle(NULL); + return GetRawDataResource(hModule, key); } base::StringPiece GetDataResource(int resource_id) { - switch (resource_id) { - case IDR_BROKENIMAGE: { - // Use webkit's broken image icon (16x16) - static unsigned char broken_image_data[] = { - 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x10, 0x00, 0x10, 0x00, 0xD5, 0x00, - 0x00, 0x20, 0x68, 0xB0, 0x70, 0x98, 0xC0, 0x28, 0x78, 0xB8, 0x20, 0x70, - 0xB8, 0x88, 0xB8, 0xD8, 0x78, 0x98, 0xC8, 0x28, 0x80, 0xC0, 0xF8, 0xF8, - 0xF8, 0xF0, 0xF0, 0xF8, 0xE0, 0xE8, 0xF0, 0xD0, 0xE0, 0xF0, 0xB8, 0xD0, - 0xE8, 0xC0, 0xD8, 0xE8, 0x98, 0xC0, 0xE0, 0x68, 0x90, 0xC0, 0x70, 0x90, - 0xC0, 0x30, 0x80, 0xC0, 0x28, 0x88, 0xC0, 0x30, 0x78, 0xB8, 0x88, 0xB8, - 0xE0, 0x78, 0xA0, 0xC8, 0x20, 0x70, 0xB0, 0x90, 0xA8, 0xD0, 0x88, 0xA8, - 0xD0, 0x70, 0x98, 0xC8, 0x90, 0xB0, 0xD0, 0x80, 0xA0, 0xC8, 0x28, 0x88, - 0xC8, 0x20, 0x78, 0xB8, 0x40, 0x90, 0xC8, 0x50, 0x98, 0xC8, 0x28, 0x78, - 0xC0, 0x50, 0x90, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x06, - 0x8F, 0xC0, 0x8C, 0x70, 0x48, 0x2C, 0x66, 0x2C, 0x87, 0xA4, 0x72, 0xB9, - 0xB4, 0x20, 0x37, 0xD0, 0xA8, 0x14, 0x7A, 0xB0, 0x5C, 0x0E, 0x51, 0x83, - 0x76, 0x1B, 0x3D, 0x5C, 0xAE, 0x91, 0x88, 0x76, 0x32, 0x59, 0x34, 0x3A, - 0x86, 0xB0, 0x57, 0x73, 0xD8, 0x12, 0x12, 0x86, 0xCE, 0x21, 0xF1, 0xD1, - 0x1E, 0x34, 0xEC, 0xAD, 0x87, 0x20, 0x80, 0x1C, 0x10, 0x02, 0x76, 0x78, - 0x6D, 0x06, 0x1F, 0x02, 0x87, 0x0D, 0x0C, 0x20, 0x1C, 0x82, 0x14, 0x07, - 0x87, 0x87, 0x1C, 0x20, 0x04, 0x1C, 0x95, 0x87, 0x07, 0x14, 0x05, 0x07, - 0x95, 0x95, 0x03, 0x12, 0x12, 0x03, 0x9C, 0x1C, 0x07, 0x05, 0x18, 0x07, - 0x03, 0xA8, 0x03, 0x15, 0x0A, 0x0A, 0x15, 0xA9, 0x03, 0x07, 0x18, 0x01, - 0xA7, 0xA9, 0xAB, 0xAD, 0xAF, 0x07, 0x01, 0x0F, 0x07, 0x15, 0xBD, 0x15, - 0x00, 0xC0, 0x00, 0xBE, 0x15, 0x07, 0x0F, 0x0E, 0xBC, 0xC3, 0xC9, 0xBD, - 0x07, 0x0E, 0xC7, 0x4C, 0xCF, 0x49, 0xCD, 0xD2, 0xD3, 0xD4, 0xD2, 0x41, - 0x00, 0x3B - }; - return base::StringPiece(reinterpret_cast(broken_image_data), - static_cast( - sizeof(broken_image_data) / sizeof(unsigned char))); - } - case IDR_TEXTAREA_RESIZER: { - // Use webkit's text area resizer image. - static unsigned char area_resizer_data[] = { - 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, - 0x08, 0x06, 0x00, 0x00, 0x00, 0xC4, 0x0F, 0xBE, 0x8B, 0x00, 0x00, 0x00, - 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0B, 0x13, 0x00, 0x00, 0x0B, - 0x13, 0x01, 0x00, 0x9A, 0x9C, 0x18, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, - 0x4D, 0x41, 0x00, 0x00, 0xB1, 0x8E, 0x7C, 0xFB, 0x51, 0x93, 0x00, 0x00, - 0x00, 0x20, 0x63, 0x48, 0x52, 0x4D, 0x00, 0x00, 0x7A, 0x25, 0x00, 0x00, - 0x80, 0x83, 0x00, 0x00, 0xF9, 0xFF, 0x00, 0x00, 0x80, 0xE9, 0x00, 0x00, - 0x75, 0x30, 0x00, 0x00, 0xEA, 0x60, 0x00, 0x00, 0x3A, 0x98, 0x00, 0x00, - 0x17, 0x6F, 0x92, 0x5F, 0xC5, 0x46, 0x00, 0x00, 0x00, 0x39, 0x49, 0x44, - 0x41, 0x54, 0x78, 0xDA, 0x7C, 0x8E, 0xB1, 0x0D, 0x00, 0x20, 0x0C, 0xC3, - 0x1C, 0x9E, 0xE5, 0xA6, 0x5E, 0x1B, 0xB6, 0x4A, 0xA0, 0xD0, 0xAC, 0x8E, - 0x25, 0xC3, 0x7F, 0x65, 0x9B, 0x35, 0xC1, 0xC9, 0x2C, 0x80, 0x74, 0x6A, - 0x98, 0x0E, 0x17, 0x7C, 0x1B, 0xCA, 0x36, 0x92, 0x76, 0x6A, 0x48, 0x66, - 0x37, 0x68, 0xAA, 0x05, 0x38, 0x03, 0x00, 0x40, 0x17, 0x33, 0x3D, 0x58, - 0x57, 0x6B, 0xB3, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, - 0x42, 0x60, 0x82 - }; - return base::StringPiece(reinterpret_cast(area_resizer_data), - static_cast( - sizeof(area_resizer_data) / sizeof(unsigned char))); - } - case IDR_SEARCH_CANCEL: - case IDR_SEARCH_CANCEL_PRESSED: - case IDR_SEARCH_MAGNIFIER: - case IDR_SEARCH_MAGNIFIER_RESULTS: - case IDR_MEDIA_PAUSE_BUTTON: - case IDR_MEDIA_PLAY_BUTTON: - case IDR_MEDIA_PLAY_BUTTON_DISABLED: - case IDR_MEDIA_SOUND_FULL_BUTTON: - case IDR_MEDIA_SOUND_NONE_BUTTON: - case IDR_MEDIA_SOUND_DISABLED: - case IDR_MEDIA_SLIDER_THUMB: - case IDR_MEDIA_VOLUME_SLIDER_THUMB: - return NetResourceProvider(resource_id); - default: - break; - } - - return base::StringPiece(); + return NetResourceProvider(resource_id); } bool GetApplicationDirectory(FilePath* path) { diff --git a/libcef/browser_webview_delegate.cc b/libcef/browser_webview_delegate.cc index 1a1a05ece..0e11c91e5 100644 --- a/libcef/browser_webview_delegate.cc +++ b/libcef/browser_webview_delegate.cc @@ -725,6 +725,21 @@ void BrowserWebViewDelegate::willSendRequest( request.setRequestorID(browser_->UIT_GetUniqueID()); } +void BrowserWebViewDelegate::reportFindInPageMatchCount( + int request_id, int count, bool final_update) +{ + browser_->UIT_NotifyFindStatus(request_id, count, gfx::Rect(), + -1, // // Don't update active match ordinal. + final_update); +} + +void BrowserWebViewDelegate::reportFindInPageSelection( + int request_id, int active_match_ordinal, const WebKit::WebRect& sel) +{ + browser_->UIT_NotifyFindStatus(request_id, -1, sel, active_match_ordinal, + false); +} + // Public methods ------------------------------------------------------------ diff --git a/libcef/browser_webview_delegate.h b/libcef/browser_webview_delegate.h index 5486354ec..8efe81d2e 100644 --- a/libcef/browser_webview_delegate.h +++ b/libcef/browser_webview_delegate.h @@ -161,6 +161,10 @@ class BrowserWebViewDelegate : public WebKit::WebViewClient, virtual void willSendRequest( WebKit::WebFrame*, unsigned identifier, WebKit::WebURLRequest&, const WebKit::WebURLResponse& redirectResponse); + virtual void reportFindInPageMatchCount( + int request_id, int count, bool final_update); + virtual void reportFindInPageSelection( + int request_id, int active_match_ordinal, const WebKit::WebRect& sel); // webkit_glue::WebPluginPageDelegate virtual webkit_glue::WebPluginDelegate* CreatePluginDelegate( diff --git a/libcef/browser_webview_delegate_win.cc b/libcef/browser_webview_delegate_win.cc index 9f1c60f0e..9befc3969 100644 --- a/libcef/browser_webview_delegate_win.cc +++ b/libcef/browser_webview_delegate_win.cc @@ -76,8 +76,10 @@ void BrowserWebViewDelegate::closeWidgetSoon() { void BrowserWebViewDelegate::didChangeCursor(const WebCursorInfo& cursor_info) { if (WebWidgetHost* host = GetWidgetHost()) { current_cursor_.InitFromCursorInfo(cursor_info); - HINSTANCE mod_handle = GetModuleHandle(NULL); - host->SetCursor(current_cursor_.GetCursor(mod_handle)); + HMODULE hModule = ::GetModuleHandle(L"libcef.dll"); + if(!hModule) + hModule = ::GetModuleHandle(NULL); + host->SetCursor(current_cursor_.GetCursor(hModule)); } } diff --git a/libcef/context.cc b/libcef/context.cc index 06d2ce603..d9dd3ef67 100644 --- a/libcef/context.cc +++ b/libcef/context.cc @@ -255,6 +255,7 @@ DWORD WINAPI ThreadHandlerUI(LPVOID lpParam) CefContext::CefContext() { + multi_threaded_message_loop_ = false; hthreadui_ = NULL; idthreadui_ = 0; heventui_ = NULL; @@ -272,8 +273,12 @@ CefContext::~CefContext() { // Just in case CefShutdown() isn't called Shutdown(); - if(at_exit_manager_) - delete at_exit_manager_; + if(!multi_threaded_message_loop_) { + if(messageloopui_) + delete messageloopui_; + if(at_exit_manager_) + delete at_exit_manager_; + } } bool CefContext::Initialize(bool multi_threaded_message_loop, @@ -378,7 +383,7 @@ bool CefContext::Initialize(bool multi_threaded_message_loop, if (!DoInitialize()) { // TODO: Process initialization errors } - // Create our own message loop there + // Message loop is scoped to the context. SetMessageLoopForUI(new CefMessageLoopForUI()); idthreadui_ = GetCurrentThreadId(); DCHECK(idthreadui_ != 0); @@ -391,6 +396,8 @@ bool CefContext::Initialize(bool multi_threaded_message_loop, Unlock(); if(initialized) { + multi_threaded_message_loop_ = multi_threaded_message_loop; + if (multi_threaded_message_loop) { // Wait for initial UI thread setup to complete WaitForSingleObject(heventui_, INFINITE); @@ -470,7 +477,6 @@ void CefContext::Shutdown() UnregisterClass(CefBrowserImpl::GetWndClass(), hinstance_); idthreadui_ = 0; - messageloopui_ = NULL; // We have exited the transitional state in_transition_ = false; diff --git a/libcef/context.h b/libcef/context.h index 7959f0655..9a1e1e23c 100644 --- a/libcef/context.h +++ b/libcef/context.h @@ -69,17 +69,22 @@ private: void DoUninitialize(); protected: + bool multi_threaded_message_loop_; + HMODULE hinstance_; DWORD idthreadui_; HANDLE hthreadui_; HANDLE heventui_; - MessageLoopForUI* messageloopui_; bool in_transition_; BrowserList browserlist_; WebPreferences* webprefs_; StatsTable* statstable_; std::wstring cache_path_; + // MessageLoopForUI will be scoped to the context when running in single + // threaded message loop mode. + MessageLoopForUI* messageloopui_; + // AtExitManager will be scoped to the context when running in single threaded // message loop mode. base::AtExitManager* at_exit_manager_; diff --git a/libcef_dll/cpptoc/browser_cpptoc.cc b/libcef_dll/cpptoc/browser_cpptoc.cc index 90205f8db..cac74e239 100644 --- a/libcef_dll/cpptoc/browser_cpptoc.cc +++ b/libcef_dll/cpptoc/browser_cpptoc.cc @@ -219,6 +219,31 @@ void CEF_CALLBACK browser_get_frame_names(struct _cef_browser_t* self, cef_string_list_append(names, stringList[i].c_str()); } +void CEF_CALLBACK browser_find(struct _cef_browser_t* self, int identifier, + const wchar_t* searchText, int forward, int matchCase, int findNext) +{ + DCHECK(self); + if(!self) + return; + + std::wstring searchTextStr; + if(searchText) + searchTextStr = searchText; + + CefBrowserCppToC::Get(self)->Find(identifier, searchTextStr, + forward?true:false, matchCase?true:false, findNext?true:false); +} + +void CEF_CALLBACK browser_stop_finding(struct _cef_browser_t* self, + int clearSelection) +{ + DCHECK(self); + if(!self) + return; + + CefBrowserCppToC::Get(self)->StopFinding(clearSelection?true:false); +} + // CONSTRUCTOR - Do not edit by hand. @@ -239,6 +264,8 @@ CefBrowserCppToC::CefBrowserCppToC(CefBrowser* cls) struct_.struct_.get_focused_frame = browser_get_focused_frame; struct_.struct_.get_frame = browser_get_frame; struct_.struct_.get_frame_names = browser_get_frame_names; + struct_.struct_.find = browser_find; + struct_.struct_.stop_finding = browser_stop_finding; } #ifdef _DEBUG diff --git a/libcef_dll/cpptoc/handler_cpptoc.cc b/libcef_dll/cpptoc/handler_cpptoc.cc index 317ecc7c1..abd0f427b 100644 --- a/libcef_dll/cpptoc/handler_cpptoc.cc +++ b/libcef_dll/cpptoc/handler_cpptoc.cc @@ -476,6 +476,22 @@ enum cef_retval_t CEF_CALLBACK handler_handle_console_message( CefBrowserCToCpp::Wrap(browser), messageStr, sourceStr, line); } +enum cef_retval_t CEF_CALLBACK handler_handle_find_result( + struct _cef_handler_t* self, cef_browser_t* browser, int identifier, + int count, const cef_rect_t* selectionRect, int activeMatchOrdinal, + int finalUpdate) +{ + DCHECK(self); + DCHECK(browser); + if(!self || !browser) + return RV_CONTINUE; + + CefRect rect(*selectionRect); + return CefHandlerCppToC::Get(self)->HandleFindResult( + CefBrowserCToCpp::Wrap(browser), identifier, count, rect, + activeMatchOrdinal, finalUpdate?true:false); +} + // CONSTRUCTOR - Do not edit by hand. @@ -506,6 +522,7 @@ CefHandlerCppToC::CefHandlerCppToC(CefHandler* cls) struct_.struct_.handle_set_focus = handler_handle_set_focus; struct_.struct_.handle_key_event = handler_handle_key_event; struct_.struct_.handle_console_message = handler_handle_console_message; + struct_.struct_.handle_find_result = handler_handle_find_result; } #ifdef _DEBUG diff --git a/libcef_dll/ctocpp/browser_ctocpp.cc b/libcef_dll/ctocpp/browser_ctocpp.cc index c7c8df138..f2effb815 100644 --- a/libcef_dll/ctocpp/browser_ctocpp.cc +++ b/libcef_dll/ctocpp/browser_ctocpp.cc @@ -176,6 +176,24 @@ void CefBrowserCToCpp::GetFrameNames(std::vector& names) cef_string_list_free(list); } +void CefBrowserCToCpp::Find(int identifier, const std::wstring& searchText, + bool forward, bool matchCase, bool findNext) +{ + if(CEF_MEMBER_MISSING(struct_, find)) + return; + + struct_->find(struct_, identifier, searchText.c_str(), forward, + matchCase, findNext); +} + +void CefBrowserCToCpp::StopFinding(bool clearSelection) +{ + if(CEF_MEMBER_MISSING(struct_, stop_finding)) + return; + + struct_->stop_finding(struct_, clearSelection); +} + #ifdef _DEBUG long CefCToCpp::DebugObjCt = 0; diff --git a/libcef_dll/ctocpp/browser_ctocpp.h b/libcef_dll/ctocpp/browser_ctocpp.h index 4c90a9231..74398f6db 100644 --- a/libcef_dll/ctocpp/browser_ctocpp.h +++ b/libcef_dll/ctocpp/browser_ctocpp.h @@ -45,6 +45,9 @@ public: virtual CefRefPtr GetFocusedFrame(); virtual CefRefPtr GetFrame(const std::wstring& name); virtual void GetFrameNames(std::vector& names); + virtual void Find(int identifier, const std::wstring& searchText, + bool forward, bool matchCase, bool findNext); + virtual void StopFinding(bool clearSelection); }; #endif // USING_CEF_SHARED diff --git a/libcef_dll/ctocpp/frame_ctocpp.cc b/libcef_dll/ctocpp/frame_ctocpp.cc index 7cfe7ee89..3a1e8fdf3 100644 --- a/libcef_dll/ctocpp/frame_ctocpp.cc +++ b/libcef_dll/ctocpp/frame_ctocpp.cc @@ -156,7 +156,7 @@ void CefFrameCToCpp::ExecuteJavaScript(const std::wstring& jsCode, const std::wstring& scriptUrl, int startLine) { if(CEF_MEMBER_MISSING(struct_, execute_java_script)) - return; + return; struct_->execute_java_script(struct_, jsCode.c_str(), scriptUrl.c_str(), startLine); @@ -165,7 +165,7 @@ void CefFrameCToCpp::ExecuteJavaScript(const std::wstring& jsCode, bool CefFrameCToCpp::IsMain() { if(CEF_MEMBER_MISSING(struct_, is_main)) - return false; + return false; return struct_->is_main(struct_)?true:false; } @@ -173,7 +173,7 @@ bool CefFrameCToCpp::IsMain() bool CefFrameCToCpp::IsFocused() { if(CEF_MEMBER_MISSING(struct_, is_focused)) - return false; + return false; return struct_->is_focused(struct_)?true:false; } diff --git a/libcef_dll/ctocpp/handler_ctocpp.cc b/libcef_dll/ctocpp/handler_ctocpp.cc index 0530f2045..342b377ca 100644 --- a/libcef_dll/ctocpp/handler_ctocpp.cc +++ b/libcef_dll/ctocpp/handler_ctocpp.cc @@ -359,6 +359,18 @@ CefHandler::RetVal CefHandlerCToCpp::HandleConsoleMessage( CefBrowserCppToC::Wrap(browser), message.c_str(), source.c_str(), line); } +CefHandler::RetVal CefHandlerCToCpp::HandleFindResult( + CefRefPtr browser, int identifier, int count, + const CefRect& selectionRect, int activeMatchOrdinal, bool finalUpdate) +{ + if(CEF_MEMBER_MISSING(struct_, handle_find_result)) + return RV_CONTINUE; + + return struct_->handle_find_result(struct_, + CefBrowserCppToC::Wrap(browser), identifier, count, &selectionRect, + activeMatchOrdinal, finalUpdate); +} + #ifdef _DEBUG long CefCToCpp::DebugObjCt = 0; diff --git a/libcef_dll/ctocpp/handler_ctocpp.h b/libcef_dll/ctocpp/handler_ctocpp.h index a8f036bac..fbfbf42ba 100644 --- a/libcef_dll/ctocpp/handler_ctocpp.h +++ b/libcef_dll/ctocpp/handler_ctocpp.h @@ -78,6 +78,9 @@ public: KeyEventType type, int code, int modifiers, bool isSystemKey); virtual RetVal HandleConsoleMessage(CefRefPtr browser, const std::wstring& message, const std::wstring& source, int line); + virtual RetVal HandleFindResult(CefRefPtr browser, int identifier, + int count, const CefRect& selectionRect, int activeMatchOrdinal, + bool finalUpdate); }; #endif // BUILDING_CEF_SHARED diff --git a/tests/cefclient/cefclient.cpp b/tests/cefclient/cefclient.cpp index e29352a33..d3ead4764 100644 --- a/tests/cefclient/cefclient.cpp +++ b/tests/cefclient/cefclient.cpp @@ -12,7 +12,7 @@ #include "thread_util.h" #include "uiplugin_test.h" #include - +#include #define MAX_LOADSTRING 100 #define MAX_URL_LENGTH 255 @@ -28,6 +28,8 @@ HINSTANCE hInst; // current instance TCHAR szTitle[MAX_LOADSTRING]; // The title bar text TCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name TCHAR szWorkingDir[MAX_PATH]; // The current working directory +UINT uFindMsg; // Message identifier for find events. +HWND hFindDlg = NULL; // Handle for the find dialog. // Forward declarations of functions included in this code module: ATOM MyRegisterClass(HINSTANCE hInstance); @@ -86,6 +88,9 @@ int APIENTRY wWinMain(HINSTANCE hInstance, hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CEFCLIENT)); + // Register the find event message. + uFindMsg = RegisterWindowMessage(FINDMSGSTRING); + // Main message loop while (GetMessage(&msg, NULL, 0, 0)) { @@ -94,6 +99,10 @@ int APIENTRY wWinMain(HINSTANCE hInstance, CefDoMessageLoopWork(); #endif + // Allow processing of find dialog messages. + if(hFindDlg && IsDialogMessage(hFindDlg, &msg)) + continue; + if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); @@ -562,6 +571,20 @@ public: return RV_HANDLED; } + // Called to report find results returned by CefBrowser::Find(). |identifer| + // is the identifier passed to CefBrowser::Find(), |count| is the number of + // matches currently identified, |selectionRect| is the location of where the + // match was found (in window coordinates), |activeMatchOrdinal| is the + // current position in the search results, and |finalUpdate| is true if this + // is the last find notification. The return value is currently ignored. + virtual RetVal HandleFindResult(CefRefPtr browser, + int identifier, int count, + const CefRect& selectionRect, + int activeMatchOrdinal, bool finalUpdate) + { + return RV_CONTINUE; + } + // Retrieve the current navigation state flags void GetNavState(bool &isLoading, bool &canGoBack, bool &canGoForward) { @@ -679,7 +702,14 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) static HWND backWnd = NULL, forwardWnd = NULL, reloadWnd = NULL, stopWnd = NULL, editWnd = NULL; static WNDPROC editWndOldProc = NULL; - + + // Static members used for the find dialog. + static FINDREPLACE fr; + static WCHAR szFindWhat[80] = {0}; + static WCHAR szLastFindWhat[80] = {0}; + static bool findNext = false; + static bool lastMatchCase = false; + int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc; @@ -708,6 +738,53 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) return (LRESULT)CallWindowProc(editWndOldProc, hWnd, message, wParam, lParam); } + else if (message == uFindMsg) + { + // Find event. + LPFINDREPLACE lpfr = (LPFINDREPLACE)lParam; + + if (lpfr->Flags & FR_DIALOGTERM) + { + // The find dialog box has been dismissed so invalidate the handle and + // reset the search results. + hFindDlg = NULL; + if(g_handler.get()) + { + g_handler->GetBrowser()->StopFinding(true); + szLastFindWhat[0] = 0; + findNext = false; + } + return 0; + } + + if ((lpfr->Flags & FR_FINDNEXT) && g_handler.get()) + { + // Search for the requested string. + bool matchCase = (lpfr->Flags & FR_MATCHCASE?true:false); + if(matchCase != lastMatchCase || + (matchCase && wcsncmp(szFindWhat, szLastFindWhat, + sizeof(szLastFindWhat)/sizeof(WCHAR)) != 0) || + (!matchCase && _wcsnicmp(szFindWhat, szLastFindWhat, + sizeof(szLastFindWhat)/sizeof(WCHAR)) != 0)) + { + // The search string has changed, so reset the search results. + if(szLastFindWhat[0] != 0) { + g_handler->GetBrowser()->StopFinding(true); + findNext = false; + } + lastMatchCase = matchCase; + wcscpy_s(szLastFindWhat, sizeof(szLastFindWhat)/sizeof(WCHAR), + szFindWhat); + } + + g_handler->GetBrowser()->Find(0, lpfr->lpstrFindWhat, + (lpfr->Flags & FR_DOWN)?true:false, matchCase, findNext); + if(!findNext) + findNext = true; + } + + return 0; + } else { // Callback for the main window @@ -824,6 +901,25 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) MB_OK | MB_ICONINFORMATION); } return 0; + case ID_FIND: + if(!hFindDlg) + { + // Create the find dialog. + ZeroMemory(&fr, sizeof(fr)); + fr.lStructSize = sizeof(fr); + fr.hwndOwner = hWnd; + fr.lpstrFindWhat = szFindWhat; + fr.wFindWhatLen = sizeof(szFindWhat); + fr.Flags = FR_HIDEWHOLEWORD | FR_DOWN; + + hFindDlg = FindText(&fr); + } + else + { + // Give focus to the existing find dialog. + ::SetFocus(hFindDlg); + } + return 0; case IDC_NAV_BACK: // Back button if(browser.get()) browser->GoBack(); diff --git a/tests/cefclient/cefclient.rc b/tests/cefclient/cefclient.rc index 430fc8827..e3a21b877 100644 --- a/tests/cefclient/cefclient.rc +++ b/tests/cefclient/cefclient.rc @@ -52,6 +52,8 @@ BEGIN POPUP "&File" BEGIN MENUITEM "E&xit", IDM_EXIT + MENUITEM SEPARATOR + MENUITEM "&Find...", ID_FIND END POPUP "&Help" BEGIN diff --git a/tests/cefclient/resource.h b/tests/cefclient/resource.h index 08dbc791d..8708fabd7 100644 --- a/tests/cefclient/resource.h +++ b/tests/cefclient/resource.h @@ -22,6 +22,7 @@ #define IDC_NAV_RELOAD 202 #define IDC_NAV_STOP 203 #define ID_WARN_CONSOLEMESSAGE 32000 +#define ID_FIND 32001 #define ID_TESTS_GETSOURCE 32769 #define ID_TESTS_GETTEXT 32770 #define ID_TESTS_JAVASCRIPT_HANDLER 32771 diff --git a/tests/unittests/test_handler.h b/tests/unittests/test_handler.h index 795102bed..41dc1c2f4 100644 --- a/tests/unittests/test_handler.h +++ b/tests/unittests/test_handler.h @@ -209,6 +209,14 @@ public: return RV_CONTINUE; } + virtual RetVal HandleFindResult(CefRefPtr browser, + int identifier, int count, + const CefRect& selectionRect, + int activeMatchOrdinal, bool finalUpdate) + { + return RV_CONTINUE; + } + CefRefPtr GetBrowser() { return browser_; diff --git a/tools/cef_parser.py b/tools/cef_parser.py index d2474f06a..ab9718762 100644 --- a/tools/cef_parser.py +++ b/tools/cef_parser.py @@ -1070,7 +1070,8 @@ class obj_analysis: 'unsigned long' : 'unsigned long', 'size_t' : 'size_t', 'bool' : 'int', - 'CefWindowHandle' : 'cef_window_handle_t' + 'CefWindowHandle' : 'cef_window_handle_t', + 'CefRect' : 'cef_rect_t', } if value in simpletypes.keys(): return {