Index: ios/web/public/test/web_view_interaction_test_util.mm |
diff --git a/ios/web/public/test/web_view_interaction_test_util.mm b/ios/web/public/test/web_view_interaction_test_util.mm |
index 0892593c062af9f446a3938cc0ca0e822540e89e..24f4b8cba8d3de4a5a7e6fcb34c781625b59b8ee 100644 |
--- a/ios/web/public/test/web_view_interaction_test_util.mm |
+++ b/ios/web/public/test/web_view_interaction_test_util.mm |
@@ -4,9 +4,13 @@ |
#import "ios/web/public/test/web_view_interaction_test_util.h" |
-#import <Foundation/Foundation.h> |
- |
+#include "base/mac/bind_objc_block.h" |
+#include "base/strings/stringprintf.h" |
+#include "base/strings/utf_string_conversions.h" |
+#include "base/test/ios/wait_util.h" |
#include "ios/testing/earl_grey/wait_util.h" |
+#import "ios/web/public/web_state/crw_web_view_scroll_view_proxy.h" |
+#import "ios/web/web_state/crw_web_view_proxy_impl.h" |
#import "ios/web/web_state/ui/crw_web_controller.h" |
#include "ios/web/web_state/web_state_impl.h" |
@@ -21,6 +25,97 @@ enum ElementAction { |
ELEMENT_ACTION_SUBMIT |
}; |
+std::unique_ptr<base::Value> ExecuteJavaScript(web::WebState* web_state, |
+ const std::string& script) { |
+ __block std::unique_ptr<base::Value> result; |
+ __block bool did_finish = false; |
+ web_state->ExecuteJavaScript(base::UTF8ToUTF16(script), |
+ base::BindBlock(^(const base::Value* value) { |
+ if (value) |
+ result = value->CreateDeepCopy(); |
+ did_finish = true; |
+ })); |
+ |
+ testing::WaitUntilCondition(testing::kWaitForJSCompletionTimeout, ^{ |
+ return did_finish; |
+ }); |
+ |
+ // As result is marked __block, this return call does a copy and not a move |
+ // (marking the variable as __block mean it is allocated in the block object |
+ // and not the stack). Since the "return std::move()" pattern is discouraged |
+ // use a local variable. |
+ // |
+ // Fixes the following compilation failure: |
+ // ../web_view_matchers.mm:ll:cc: error: call to implicitly-deleted copy |
+ // constructor of 'std::unique_ptr<base::Value>' |
+ std::unique_ptr<base::Value> stack_result = std::move(result); |
+ return stack_result; |
+} |
+ |
+CGRect GetBoundingRectOfElementWithId(web::WebState* web_state, |
+ const std::string& element_id) { |
+ std::string kGetBoundsScript = |
+ "(function() {" |
+ " var element = document.getElementById('" + |
+ element_id + |
+ "');" |
+ " if (!element)" |
+ " return {'error': 'Element " + |
+ element_id + |
+ " not found'};" |
+ " var rect = element.getBoundingClientRect();" |
+ " var top = rect.top + document.body.scrollTop;" |
+ " var bottom = rect.bottom + document.body.scrollTop;" |
+ " var left = rect.left + document.body.scrollLeft;" |
+ " var right = rect.right + document.body.scrollLeft;" |
+ " return {" |
+ " 'left': left," |
+ " 'top': top," |
+ " 'width': right - left," |
+ " 'height': bottom - top," |
+ " 'document_width' : document.documentElement.scrollWidth," |
+ " 'document_height' : document.documentElement.scrollHeight," |
+ " };" |
+ "})();"; |
+ |
+ base::DictionaryValue const* rect = nullptr; |
+ bool found = false; |
+ NSDate* deadline = |
+ [NSDate dateWithTimeIntervalSinceNow:testing::kWaitForUIElementTimeout]; |
+ while (([[NSDate date] compare:deadline] != NSOrderedDescending) && !found) { |
+ std::unique_ptr<base::Value> value = |
+ ExecuteJavaScript(web_state, kGetBoundsScript); |
+ base::DictionaryValue* dictionary = nullptr; |
+ if (value && value->GetAsDictionary(&dictionary)) { |
+ std::string error; |
+ if (dictionary->GetString("error", &error)) { |
+ DLOG(ERROR) << "Error getting rect: " << error << ", retrying.."; |
+ } else { |
+ rect = dictionary->DeepCopy(); |
+ found = true; |
+ } |
+ } |
+ base::test::ios::SpinRunLoopWithMaxDelay( |
+ base::TimeDelta::FromSecondsD(testing::kSpinDelaySeconds)); |
+ } |
+ |
+ if (!found) |
+ return CGRectNull; |
+ |
+ double left, top, width, height, document_width, document_height; |
+ if (!(rect->GetDouble("left", &left) && rect->GetDouble("top", &top) && |
+ rect->GetDouble("width", &width) && |
+ rect->GetDouble("height", &height) && |
+ rect->GetDouble("document_width", &document_width) && |
+ rect->GetDouble("document_height", &document_height))) { |
+ return CGRectNull; |
+ } |
+ |
+ CGFloat scale = [[web_state->GetWebViewProxy() scrollViewProxy] zoomScale]; |
+ |
+ return CGRectMake(left * scale, top * scale, width * scale, height * scale); |
+} |
+ |
// Returns whether the Javascript action specified by |action| ran on |
// |element_id| in the passed |web_state|. |
bool RunActionOnWebViewElementWithId(web::WebState* web_state, |