summaryrefslogtreecommitdiff
path: root/chromium/components/autofill/ios/form_util/form_activity_tab_helper.mm
blob: 0a30ff60405d2839707b9874b9a57227ce37b9e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/autofill/ios/form_util/form_activity_tab_helper.h"

#import <Foundation/Foundation.h>

#include "base/values.h"
#include "components/autofill/ios/form_util/form_activity_observer.h"
#include "ios/web/public/web_state/form_activity_params.h"

#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif

DEFINE_WEB_STATE_USER_DATA_KEY(autofill::FormActivityTabHelper);

namespace autofill {

namespace {
// Prefix for the form activity event commands. Must be kept in sync with
// form.js.
const char kCommandPrefix[] = "form";
}

// static
FormActivityTabHelper* FormActivityTabHelper::GetOrCreateForWebState(
    web::WebState* web_state) {
  FormActivityTabHelper* helper = FromWebState(web_state);
  if (!helper) {
    CreateForWebState(web_state);
    helper = FromWebState(web_state);
    DCHECK(helper);
  }
  return helper;
}

FormActivityTabHelper::FormActivityTabHelper(web::WebState* web_state)
    : web_state_(web_state) {
  web_state_->AddObserver(this);
  web_state_->AddScriptCommandCallback(
      base::BindRepeating(&FormActivityTabHelper::OnFormCommand,
                          base::Unretained(this)),
      kCommandPrefix);
}

FormActivityTabHelper::~FormActivityTabHelper() {
  if (web_state_) {
    web_state_->RemoveObserver(this);
    web_state_->RemoveScriptCommandCallback(kCommandPrefix);
    web_state_ = nullptr;
  }
}

void FormActivityTabHelper::AddObserver(FormActivityObserver* observer) {
  observers_.AddObserver(observer);
}

void FormActivityTabHelper::RemoveObserver(FormActivityObserver* observer) {
  observers_.RemoveObserver(observer);
}

bool FormActivityTabHelper::OnFormCommand(const base::DictionaryValue& message,
                                          const GURL& url,
                                          bool has_user_gesture,
                                          bool form_in_main_frame) {
  std::string command;
  if (!message.GetString("command", &command)) {
    DLOG(WARNING) << "JS message parameter not found: command";
    return NO;
  }
  if (command == "form.submit") {
    return FormSubmissionHandler(message, has_user_gesture, form_in_main_frame);
  }
  if (command == "form.activity") {
    return HandleFormActivity(message, has_user_gesture, form_in_main_frame);
  }
  return false;
}

bool FormActivityTabHelper::HandleFormActivity(
    const base::DictionaryValue& message,
    bool has_user_gesture,
    bool form_in_main_frame) {
  web::FormActivityParams params;
  if (!message.GetString("formName", &params.form_name) ||
      !message.GetString("fieldName", &params.field_name) ||
      !message.GetString("fieldIdentifier", &params.field_identifier) ||
      !message.GetString("fieldType", &params.field_type) ||
      !message.GetString("type", &params.type) ||
      !message.GetString("value", &params.value) ||
      !message.GetBoolean("hasUserGesture", &params.has_user_gesture)) {
    params.input_missing = true;
  }

  params.is_main_frame = form_in_main_frame;
  for (auto& observer : observers_)
    observer.OnFormActivity(web_state_, params);
  return true;
}

bool FormActivityTabHelper::FormSubmissionHandler(
    const base::DictionaryValue& message,
    bool has_user_gesture,
    bool form_in_main_frame) {
  std::string href;
  if (!message.GetString("href", &href)) {
    DLOG(WARNING) << "JS message parameter not found: href";
    return false;
  }
  std::string form_name;
  message.GetString("formName", &form_name);
  // We decide the form is user-submitted if the user has interacted with
  // the main page (using logic from the popup blocker), or if the keyboard
  // is visible.
  BOOL submitted_by_user =
      has_user_gesture || [web_state_->GetWebViewProxy() keyboardAccessory];

  for (auto& observer : observers_)
    observer.DidSubmitDocument(web_state_, form_name, submitted_by_user,
                               form_in_main_frame);
  return true;
}

void FormActivityTabHelper::WebStateDestroyed(web::WebState* web_state) {
  DCHECK_EQ(web_state_, web_state);
  web_state_->RemoveScriptCommandCallback(kCommandPrefix);
  web_state_->RemoveObserver(this);
  web_state_ = nullptr;
}

}  // namespace autofill