1// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#import "ios/chrome/browser/ui/dialogs/overlay_java_script_dialog_presenter.h"
6
7#include "base/bind.h"
8#include "base/metrics/histogram_macros.h"
9#import "base/strings/sys_string_conversions.h"
10#include "ios/chrome/browser/overlays/public/overlay_callback_manager.h"
11#import "ios/chrome/browser/overlays/public/overlay_request.h"
12#import "ios/chrome/browser/overlays/public/overlay_request_queue.h"
13#import "ios/chrome/browser/overlays/public/overlay_response.h"
14#import "ios/chrome/browser/overlays/public/web_content_area/java_script_dialog_overlay.h"
15#import "ios/chrome/browser/ui/dialogs/java_script_dialog_blocking_state.h"
16#include "ios/chrome/browser/ui/dialogs/java_script_dialog_metrics.h"
17#import "ios/web/public/web_state.h"
18
19#if !defined(__has_feature) || !__has_feature(objc_arc)
20#error "This file requires ARC support."
21#endif
22
23using java_script_dialog_overlays::JavaScriptDialogRequest;
24using java_script_dialog_overlays::JavaScriptDialogResponse;
25
26namespace {
27// Completion callback for JavaScript dialog overlays.
28void HandleJavaScriptDialogResponse(web::DialogClosedCallback callback,
29                                    web::WebState::Getter web_state_getter,
30                                    OverlayResponse* response) {
31  // Notify the blocking state that the dialog was shown.
32  web::WebState* web_state = web_state_getter.Run();
33  JavaScriptDialogBlockingState* blocking_state =
34      web_state ? JavaScriptDialogBlockingState::FromWebState(web_state)
35                : nullptr;
36  if (blocking_state)
37    blocking_state->JavaScriptDialogWasShown();
38
39  JavaScriptDialogResponse* dialog_response =
40      response ? response->GetInfo<JavaScriptDialogResponse>() : nullptr;
41  if (!dialog_response) {
42    // A null response is used if the dialog was not closed by user interaction.
43    // This occurs either for navigation or because of WebState closures.
44    IOSJavaScriptDialogDismissalCause cause =
45        web_state ? IOSJavaScriptDialogDismissalCause::kNavigation
46                  : IOSJavaScriptDialogDismissalCause::kClosure;
47    RecordDialogDismissalCause(cause);
48    std::move(callback).Run(/*success=*/false, /*user_input=*/nil);
49    return;
50  }
51
52  // Update the blocking state if the suppression action was selected.
53  JavaScriptDialogResponse::Action action = dialog_response->action();
54  if (blocking_state &&
55      action == JavaScriptDialogResponse::Action::kBlockDialogs) {
56    blocking_state->JavaScriptDialogBlockingOptionSelected();
57  }
58
59  RecordDialogDismissalCause(IOSJavaScriptDialogDismissalCause::kUser);
60  bool confirmed = action == JavaScriptDialogResponse::Action::kConfirm;
61  NSString* user_input = confirmed ? dialog_response->user_input() : nil;
62  std::move(callback).Run(confirmed, user_input);
63}
64}  // namespace
65
66OverlayJavaScriptDialogPresenter::OverlayJavaScriptDialogPresenter() = default;
67
68OverlayJavaScriptDialogPresenter::OverlayJavaScriptDialogPresenter(
69    OverlayJavaScriptDialogPresenter&& other) = default;
70
71OverlayJavaScriptDialogPresenter& OverlayJavaScriptDialogPresenter::operator=(
72    OverlayJavaScriptDialogPresenter&& other) = default;
73
74OverlayJavaScriptDialogPresenter::~OverlayJavaScriptDialogPresenter() = default;
75
76void OverlayJavaScriptDialogPresenter::RunJavaScriptDialog(
77    web::WebState* web_state,
78    const GURL& origin_url,
79    web::JavaScriptDialogType dialog_type,
80    NSString* message_text,
81    NSString* default_prompt_text,
82    web::DialogClosedCallback callback) {
83  JavaScriptDialogBlockingState::CreateForWebState(web_state);
84  if (JavaScriptDialogBlockingState::FromWebState(web_state)->blocked()) {
85    // Block the dialog if needed.
86    RecordDialogDismissalCause(IOSJavaScriptDialogDismissalCause::kBlocked);
87    std::move(callback).Run(NO, nil);
88    return;
89  }
90
91  bool from_main_frame_origin =
92      origin_url.GetOrigin() == web_state->GetLastCommittedURL().GetOrigin();
93  std::unique_ptr<OverlayRequest> request =
94      OverlayRequest::CreateWithConfig<JavaScriptDialogRequest>(
95          dialog_type, web_state, origin_url, from_main_frame_origin,
96          message_text, default_prompt_text);
97  request->GetCallbackManager()->AddCompletionCallback(
98      base::BindOnce(&HandleJavaScriptDialogResponse, std::move(callback),
99                     web_state->CreateDefaultGetter()));
100  OverlayRequestQueue::FromWebState(web_state, OverlayModality::kWebContentArea)
101      ->AddRequest(std::move(request));
102}
103
104void OverlayJavaScriptDialogPresenter::CancelDialogs(web::WebState* web_state) {
105  OverlayRequestQueue::FromWebState(web_state, OverlayModality::kWebContentArea)
106      ->CancelAllRequests();
107}
108