blob: c88c094f79b7a9ab2eb2f0461c93b685561e62ce [file] [log] [blame]
// Copyright 2016 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.
#import "ios/chrome/browser/ui/dialogs/javascript_dialog_blocking_util.h"
#import <objc/runtime.h>
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "ios/web/public/web_state/web_state.h"
#include "ios/web/public/web_state/web_state_observer.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// The key under which JavaScriptDialogBlockingStates are associated with
// their WebStates.
const void* const kBlockingStateKey = &kBlockingStateKey;
}
#pragma mark - JavaScriptDialogBlockingStateWrapper
class JavaScriptDialogBlockingState;
// Wrapper class used to associated JavaScriptDialogBlockingStates with their
// WebStates.
class JavaScriptDialogBlockingStateWrapper
: public base::SupportsUserData::Data {
public:
// Factory method. Guaranteed to return a non-null value.
static JavaScriptDialogBlockingStateWrapper* FromWebState(
web::WebState* web_state) {
DCHECK(web_state);
JavaScriptDialogBlockingStateWrapper* wrapper =
static_cast<JavaScriptDialogBlockingStateWrapper*>(
web_state->GetUserData(kBlockingStateKey));
if (!wrapper)
wrapper = new JavaScriptDialogBlockingStateWrapper(web_state);
return wrapper;
}
// The JavaScriptDialogBlockingState owned by this wrapper.
JavaScriptDialogBlockingState* state() const { return state_.get(); }
void set_state(JavaScriptDialogBlockingState* state) { state_.reset(state); }
private:
std::unique_ptr<JavaScriptDialogBlockingState> state_;
// Private constructor associating the newly created object with |web_state|
// via its SupportsUserData.
explicit JavaScriptDialogBlockingStateWrapper(web::WebState* web_state)
: state_(nullptr) {
DCHECK(web_state);
web_state->SetUserData(kBlockingStateKey, base::WrapUnique(this));
}
};
#pragma mark - JavaScriptDialogBlockingState
// Wrapper class used to associated JavaScriptDialogBlockingStates with their
// WebStates.
class JavaScriptDialogBlockingState : public web::WebStateObserver {
public:
// Factory method. Guaranteed to return a non-null value.
static JavaScriptDialogBlockingState* FromWebState(web::WebState* web_state) {
DCHECK(web_state);
JavaScriptDialogBlockingStateWrapper* wrapper =
JavaScriptDialogBlockingStateWrapper::FromWebState(web_state);
if (!wrapper->state())
wrapper->set_state(new JavaScriptDialogBlockingState(web_state));
return wrapper->state();
}
// Whether JavaScript dialogs should be blocked for the WebState.
bool ShouldBlockDialogs() const { return should_block_dialogs_; }
void SetShouldBlockDialogs(bool should_block_dialogs) {
should_block_dialogs_ = should_block_dialogs;
}
// Whether the blocking option should be shown for the next JavaScript dialog
// presented by the WebState.
bool ShouldShowBlockingOption() const { return dialog_count_ >= 1; }
// Called whenever a JavaScript dialog is displayed by the WebState.
void DialogWasShown() { dialog_count_++; }
// WebStateObserver overrides:
void NavigationItemCommitted(
const web::LoadCommittedDetails& load_details) override {
// The blocking state should be reset after each page load.
dialog_count_ = 0;
should_block_dialogs_ = false;
}
void WebStateDestroyed() override {
// Stop observing the WebState when it's destroyed since this object will be
// deleted after its WebState's destructor. This avoids calling
// RemoveObserver() on a deallocated WebState from the WebStateObserver
// destructor.
Observe(nullptr);
}
protected:
// Constructor that begins observing |web_state|.
explicit JavaScriptDialogBlockingState(web::WebState* web_state)
: web::WebStateObserver(web_state),
dialog_count_(0),
should_block_dialogs_(false) {}
private:
size_t dialog_count_;
bool should_block_dialogs_;
};
#pragma mark - javascript_dialog_blocking
bool ShouldShowDialogBlockingOption(web::WebState* web_state) {
DCHECK(web_state);
return JavaScriptDialogBlockingState::FromWebState(web_state)
->ShouldShowBlockingOption();
}
void JavaScriptDialogWasShown(web::WebState* web_state) {
DCHECK(web_state);
JavaScriptDialogBlockingState::FromWebState(web_state)->DialogWasShown();
}
bool ShouldBlockJavaScriptDialogs(web::WebState* web_state) {
DCHECK(web_state);
return JavaScriptDialogBlockingState::FromWebState(web_state)
->ShouldBlockDialogs();
}
void DialogBlockingOptionSelected(web::WebState* web_state) {
DCHECK(web_state);
JavaScriptDialogBlockingState::FromWebState(web_state)->SetShouldBlockDialogs(
true);
}