// Copyright (c) 2012 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 "chrome/browser/ui/webui/chromeos/mobile_setup_ui.h"

#include <stddef.h>

#include <memory>

#include <algorithm>
#include <map>
#include <string>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ref_counted_memory.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/mobile/mobile_activator.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/browser_resources.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/network/device_state.h"
#include "chromeos/network/network_configuration_handler.h"
#include "chromeos/network/network_event_log.h"
#include "chromeos/network/network_state.h"
#include "chromeos/network/network_state_handler.h"
#include "chromeos/network/network_state_handler_observer.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/url_data_source.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_message_handler.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/webui/jstemplate_builder.h"
#include "ui/base/webui/web_ui_util.h"
#include "url/gurl.h"

using chromeos::MobileActivator;
using chromeos::NetworkHandler;
using chromeos::NetworkState;
using content::BrowserThread;
using content::RenderViewHost;
using content::WebContents;
using content::WebUIMessageHandler;

namespace {

// Host page JS API function names.
const char kJsApiStartActivation[] = "startActivation";
const char kJsApiSetTransactionStatus[] = "setTransactionStatus";
const char kJsApiPaymentPortalLoad[] = "paymentPortalLoad";
const char kJsGetDeviceInfo[] = "getDeviceInfo";
const char kJsApiResultOK[] = "ok";

const char kJsDeviceStatusChangedCallback[] =
    "mobile.MobileSetup.deviceStateChanged";
const char kJsPortalFrameLoadFailedCallback[] =
    "mobile.MobileSetup.portalFrameLoadError";
const char kJsPortalFrameLoadCompletedCallback[] =
    "mobile.MobileSetup.portalFrameLoadCompleted";
const char kJsGetDeviceInfoCallback[] =
    "mobile.MobileSetupPortal.onGotDeviceInfo";
const char kJsConnectivityChangedCallback[] =
    "mobile.MobileSetupPortal.onConnectivityChanged";

void DataRequestFailed(
    const std::string& service_path,
    const content::URLDataSource::GotDataCallback& callback) {
  NET_LOG_ERROR("Data Request Failed for Mobile Setup", service_path);
  scoped_refptr<base::RefCountedBytes> html_bytes(new base::RefCountedBytes);
  callback.Run(html_bytes.get());
}

// Converts the network properties into a JS object.
void GetDeviceInfo(const base::DictionaryValue& properties,
                   base::DictionaryValue* value) {
  std::string name;
  properties.GetStringWithoutPathExpansion(shill::kNameProperty, &name);
  std::string activation_type;
  properties.GetStringWithoutPathExpansion(
      shill::kActivationTypeProperty,
      &activation_type);
  const base::DictionaryValue* payment_dict;
  std::string payment_url, post_method, post_data;
  if (properties.GetDictionaryWithoutPathExpansion(
          shill::kPaymentPortalProperty, &payment_dict)) {
    payment_dict->GetStringWithoutPathExpansion(
        shill::kPaymentPortalURL, &payment_url);
    payment_dict->GetStringWithoutPathExpansion(
        shill::kPaymentPortalMethod, &post_method);
    payment_dict->GetStringWithoutPathExpansion(
        shill::kPaymentPortalPostData, &post_data);
  }

  value->SetString("activation_type", activation_type);
  value->SetString("carrier", name);
  value->SetString("payment_url", payment_url);
  if (base::LowerCaseEqualsASCII(post_method, "post") && !post_data.empty())
    value->SetString("post_data", post_data);

  // Use the cached DeviceState properties.
  std::string device_path;
  if (!properties.GetStringWithoutPathExpansion(
          shill::kDeviceProperty, &device_path) ||
      device_path.empty()) {
    return;
  }
  const chromeos::DeviceState* device =
      NetworkHandler::Get()->network_state_handler()->GetDeviceState(
          device_path);
  if (!device)
    return;

  value->SetString("MEID", device->meid());
  value->SetString("IMEI", device->imei());
  value->SetString("MDN", device->mdn());
}

void SetActivationStateAndError(MobileActivator::PlanActivationState state,
                                const std::string& error_description,
                                base::DictionaryValue* value) {
  value->SetInteger("state", state);
  if (!error_description.empty())
    value->SetString("error", error_description);
}

}  // namespace

class MobileSetupUIHTMLSource : public content::URLDataSource {
 public:
  MobileSetupUIHTMLSource();

  // content::URLDataSource implementation.
  std::string GetSource() const override;
  void StartDataRequest(
      const std::string& path,
      const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
      const content::URLDataSource::GotDataCallback& callback) override;
  std::string GetMimeType(const std::string&) const override {
    return "text/html";
  }
  bool ShouldAddContentSecurityPolicy() const override { return false; }
  bool AllowCaching() const override {
    // Should not be cached to reflect dynamically-generated contents that may
    // depend on current settings.
    return false;
  }

 private:
  ~MobileSetupUIHTMLSource() override {}

  void GetPropertiesAndStartDataRequest(
      const content::URLDataSource::GotDataCallback& callback,
      const std::string& service_path,
      const base::DictionaryValue& properties);
  void GetPropertiesFailure(
      const content::URLDataSource::GotDataCallback& callback,
      const std::string& service_path,
      const std::string& error_name,
      std::unique_ptr<base::DictionaryValue> error_data);

  base::WeakPtrFactory<MobileSetupUIHTMLSource> weak_ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(MobileSetupUIHTMLSource);
};

// The handler for Javascript messages related to the "register" view.
class MobileSetupHandler
  : public WebUIMessageHandler,
    public MobileActivator::Observer,
    public chromeos::NetworkStateHandlerObserver,
    public base::SupportsWeakPtr<MobileSetupHandler> {
 public:
  MobileSetupHandler();
  ~MobileSetupHandler() override;

  // WebUIMessageHandler implementation.
  void RegisterMessages() override;

 private:
  enum Type {
    TYPE_UNDETERMINED,
    // The network is not yet activated, and the webui is in activation flow.
    TYPE_ACTIVATION,
    // The network is activated, the webui displays network portal.
    TYPE_PORTAL,
    // Same as TYPE_PORTAL, but the network technology is LTE. The webui is
    // additionally aware of network manager state and whether the portal can be
    // reached.
    TYPE_PORTAL_LTE
  };

  // MobileActivator::Observer.
  void OnActivationStateChanged(const NetworkState* network,
                                MobileActivator::PlanActivationState new_state,
                                const std::string& error_description) override;

  // Callbacks for NetworkConfigurationHandler::GetProperties.
  void GetPropertiesAndCallStatusChanged(
      MobileActivator::PlanActivationState state,
      const std::string& error_description,
      const std::string& service_path,
      const base::DictionaryValue& properties);
  void GetPropertiesAndCallGetDeviceInfo(
      const std::string& service_path,
      const base::DictionaryValue& properties);
  void GetPropertiesFailure(const std::string& service_path,
                            const std::string& callback_name,
                            const std::string& error_name,
                            std::unique_ptr<base::DictionaryValue> error_data);

  // Handlers for JS WebUI messages.
  void HandleSetTransactionStatus(const base::ListValue* args);
  void HandleStartActivation(const base::ListValue* args);
  void HandlePaymentPortalLoad(const base::ListValue* args);
  void HandleGetDeviceInfo(const base::ListValue* args);

  // NetworkStateHandlerObserver implementation.
  void NetworkConnectionStateChanged(const NetworkState* network) override;
  void DefaultNetworkChanged(const NetworkState* default_network) override;

  // Updates |lte_portal_reachable_| for lte network |network| and notifies
  // webui of the new state if the reachability changed or |force_notification|
  // is set.
  void UpdatePortalReachability(const NetworkState* network,
                                bool force_notification);

  // Sends message to host registration page with system/user info data.
  void SendDeviceInfo();

  // Type of the mobilesetup webui deduced from received messages.
  Type type_;
  // Whether portal page for lte networks can be reached in current network
  // connection state. This value is reflected in portal webui for lte networks.
  // Initial value is true.
  bool lte_portal_reachable_;
  base::WeakPtrFactory<MobileSetupHandler> weak_ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(MobileSetupHandler);
};

////////////////////////////////////////////////////////////////////////////////
//
// MobileSetupUIHTMLSource
//
////////////////////////////////////////////////////////////////////////////////

MobileSetupUIHTMLSource::MobileSetupUIHTMLSource()
    : weak_ptr_factory_(this) {
}

std::string MobileSetupUIHTMLSource::GetSource() const {
  return chrome::kChromeUIMobileSetupHost;
}

void MobileSetupUIHTMLSource::StartDataRequest(
    const std::string& path,
    const content::ResourceRequestInfo::WebContentsGetter& wc_getter,
    const content::URLDataSource::GotDataCallback& callback) {
  NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
      path,
      base::Bind(&MobileSetupUIHTMLSource::GetPropertiesAndStartDataRequest,
                 weak_ptr_factory_.GetWeakPtr(), callback),
      base::Bind(&MobileSetupUIHTMLSource::GetPropertiesFailure,
                 weak_ptr_factory_.GetWeakPtr(), callback, path));
}

void MobileSetupUIHTMLSource::GetPropertiesAndStartDataRequest(
    const content::URLDataSource::GotDataCallback& callback,
    const std::string& service_path,
    const base::DictionaryValue& properties) {
  const base::DictionaryValue* payment_dict;
  std::string name, usage_url, activation_state, payment_url;
  if (!properties.GetStringWithoutPathExpansion(
          shill::kNameProperty, &name) ||
      !properties.GetStringWithoutPathExpansion(
          shill::kUsageURLProperty, &usage_url) ||
      !properties.GetStringWithoutPathExpansion(
          shill::kActivationStateProperty, &activation_state) ||
      !properties.GetDictionaryWithoutPathExpansion(
          shill::kPaymentPortalProperty, &payment_dict) ||
      !payment_dict->GetStringWithoutPathExpansion(
          shill::kPaymentPortalURL, &payment_url)) {
    DataRequestFailed(service_path, callback);
    return;
  }

  if (payment_url.empty() && usage_url.empty() &&
      activation_state != shill::kActivationStateActivated) {
    DataRequestFailed(service_path, callback);
    return;
  }

  NET_LOG_EVENT("Starting mobile setup", service_path);
  base::DictionaryValue strings;

  strings.SetString("connecting_header",
                    l10n_util::GetStringFUTF16(IDS_MOBILE_CONNECTING_HEADER,
                                               base::UTF8ToUTF16(name)));
  strings.SetString("error_header",
                    l10n_util::GetStringUTF16(IDS_MOBILE_ERROR_HEADER));
  strings.SetString("activating_header",
                    l10n_util::GetStringUTF16(IDS_MOBILE_ACTIVATING_HEADER));
  strings.SetString("completed_header",
                    l10n_util::GetStringUTF16(IDS_MOBILE_COMPLETED_HEADER));
  strings.SetString("please_wait",
                    l10n_util::GetStringUTF16(IDS_MOBILE_PLEASE_WAIT));
  strings.SetString("completed_text",
                    l10n_util::GetStringUTF16(IDS_MOBILE_COMPLETED_TEXT));
  strings.SetString("portal_unreachable_header",
                    l10n_util::GetStringUTF16(IDS_MOBILE_NO_CONNECTION_HEADER));
  strings.SetString("invalid_device_info_header",
      l10n_util::GetStringUTF16(IDS_MOBILE_INVALID_DEVICE_INFO_HEADER));
  strings.SetString("title", l10n_util::GetStringUTF16(IDS_MOBILE_SETUP_TITLE));
  strings.SetString("close_button",
                    l10n_util::GetStringUTF16(IDS_CLOSE));
  strings.SetString("cancel_button",
                    l10n_util::GetStringUTF16(IDS_CANCEL));
  strings.SetString("ok_button",
                    l10n_util::GetStringUTF16(IDS_OK));

  const std::string& app_locale = g_browser_process->GetApplicationLocale();
  webui::SetLoadTimeDataDefaults(app_locale, &strings);

  // The webui differs based on whether the network is activated or not. If the
  // network is activated, the webui goes straight to portal. Otherwise the
  // webui is used for activation flow.
  std::string full_html;
  if (activation_state == shill::kActivationStateActivated) {
    static const base::StringPiece html_for_activated(
        ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
            IDR_MOBILE_SETUP_PORTAL_PAGE_HTML));
    full_html = webui::GetI18nTemplateHtml(html_for_activated, &strings);
  } else {
    static const base::StringPiece html_for_non_activated(
        ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
            IDR_MOBILE_SETUP_PAGE_HTML));
    full_html = webui::GetI18nTemplateHtml(html_for_non_activated, &strings);
  }

  callback.Run(base::RefCountedString::TakeString(&full_html));
}

void MobileSetupUIHTMLSource::GetPropertiesFailure(
    const content::URLDataSource::GotDataCallback& callback,
    const std::string& service_path,
    const std::string& error_name,
    std::unique_ptr<base::DictionaryValue> error_data) {
  DataRequestFailed(service_path, callback);
}

////////////////////////////////////////////////////////////////////////////////
//
// MobileSetupHandler
//
////////////////////////////////////////////////////////////////////////////////
MobileSetupHandler::MobileSetupHandler()
    : type_(TYPE_UNDETERMINED),
      lte_portal_reachable_(true),
      weak_ptr_factory_(this) {
}

MobileSetupHandler::~MobileSetupHandler() {
  if (type_ == TYPE_ACTIVATION) {
    MobileActivator::GetInstance()->RemoveObserver(this);
    MobileActivator::GetInstance()->TerminateActivation();
  } else if (type_ == TYPE_PORTAL_LTE) {
    NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
                                                                   FROM_HERE);
  }
}

void MobileSetupHandler::OnActivationStateChanged(
    const NetworkState* network,
    MobileActivator::PlanActivationState state,
    const std::string& error_description) {
  DCHECK_EQ(TYPE_ACTIVATION, type_);
  if (!web_ui())
    return;

  if (!network) {
    base::DictionaryValue device_dict;
    SetActivationStateAndError(state, error_description, &device_dict);
    web_ui()->CallJavascriptFunctionUnsafe(kJsDeviceStatusChangedCallback,
                                           device_dict);
    return;
  }

  NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
      network->path(),
      base::Bind(&MobileSetupHandler::GetPropertiesAndCallStatusChanged,
                 weak_ptr_factory_.GetWeakPtr(), state, error_description),
      base::Bind(&MobileSetupHandler::GetPropertiesFailure,
                 weak_ptr_factory_.GetWeakPtr(), network->path(),
                 kJsDeviceStatusChangedCallback));
}

void MobileSetupHandler::GetPropertiesAndCallStatusChanged(
    MobileActivator::PlanActivationState state,
    const std::string& error_description,
    const std::string& service_path,
    const base::DictionaryValue& properties) {
  base::DictionaryValue device_dict;
  GetDeviceInfo(properties, &device_dict);
  SetActivationStateAndError(state, error_description, &device_dict);
  web_ui()->CallJavascriptFunctionUnsafe(kJsDeviceStatusChangedCallback,
                                         device_dict);
}

void MobileSetupHandler::RegisterMessages() {
  web_ui()->RegisterMessageCallback(
      kJsApiStartActivation,
      base::BindRepeating(&MobileSetupHandler::HandleStartActivation,
                          base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      kJsApiSetTransactionStatus,
      base::BindRepeating(&MobileSetupHandler::HandleSetTransactionStatus,
                          base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      kJsApiPaymentPortalLoad,
      base::BindRepeating(&MobileSetupHandler::HandlePaymentPortalLoad,
                          base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      kJsGetDeviceInfo,
      base::BindRepeating(&MobileSetupHandler::HandleGetDeviceInfo,
                          base::Unretained(this)));
}

void MobileSetupHandler::HandleStartActivation(const base::ListValue* args) {
  DCHECK_EQ(TYPE_UNDETERMINED, type_);

  if (!web_ui())
    return;

  std::string path = web_ui()->GetWebContents()->GetURL().path();
  if (path.empty())
    return;

  LOG(WARNING) << "Starting activation for service " << path;

  type_ = TYPE_ACTIVATION;
  MobileActivator::GetInstance()->AddObserver(this);
  MobileActivator::GetInstance()->InitiateActivation(path.substr(1));
}

void MobileSetupHandler::HandleSetTransactionStatus(
    const base::ListValue* args) {
  DCHECK_EQ(TYPE_ACTIVATION, type_);
  if (!web_ui())
    return;

  const size_t kSetTransactionStatusParamCount = 1;
  if (args->GetSize() != kSetTransactionStatusParamCount)
    return;
  // Get change callback function name.
  std::string status;
  if (!args->GetString(0, &status))
    return;

  MobileActivator::GetInstance()->OnSetTransactionStatus(
      base::LowerCaseEqualsASCII(status, kJsApiResultOK));
}

void MobileSetupHandler::HandlePaymentPortalLoad(const base::ListValue* args) {
  // Only activation flow webui is interested in these events.
  if (type_ != TYPE_ACTIVATION || !web_ui())
    return;

  const size_t kPaymentPortalLoadParamCount = 1;
  if (args->GetSize() != kPaymentPortalLoadParamCount)
    return;
  // Get change callback function name.
  std::string result;
  if (!args->GetString(0, &result))
    return;

  MobileActivator::GetInstance()->OnPortalLoaded(
      base::LowerCaseEqualsASCII(result, kJsApiResultOK));
}

void MobileSetupHandler::HandleGetDeviceInfo(const base::ListValue* args) {
  DCHECK_NE(TYPE_ACTIVATION, type_);
  if (!web_ui())
    return;

  std::string path = web_ui()->GetWebContents()->GetURL().path();
  if (path.empty())
    return;

  chromeos::NetworkStateHandler* nsh =
      NetworkHandler::Get()->network_state_handler();
  // TODO: Figure out why the path has an extra '/' in the front. (e.g. It is
  // '//service/5' instead of '/service/5'.
  const NetworkState* network = nsh->GetNetworkState(path.substr(1));
  if (!network) {
    web_ui()->GetWebContents()->Close();
    return;
  }

  // If this is the initial call, update the network status and start observing
  // network changes, but only for LTE networks. The other networks should
  // ignore network status.
  if (type_ == TYPE_UNDETERMINED) {
    if (network->network_technology() == shill::kNetworkTechnologyLte ||
        network->network_technology() == shill::kNetworkTechnologyLteAdvanced) {
      type_ = TYPE_PORTAL_LTE;
      nsh->AddObserver(this, FROM_HERE);
      // Update the network status and notify the webui. This is the initial
      // network state so the webui should be notified no matter what.
      UpdatePortalReachability(network,
                               true /* force notification */);
    } else {
      type_ = TYPE_PORTAL;
      // For non-LTE networks network state is ignored, so report the portal is
      // reachable, so it gets shown.
      web_ui()->CallJavascriptFunctionUnsafe(kJsConnectivityChangedCallback,
                                             base::Value(true));
    }
  }

  NetworkHandler::Get()->network_configuration_handler()->GetShillProperties(
      network->path(),
      base::Bind(&MobileSetupHandler::GetPropertiesAndCallGetDeviceInfo,
                 weak_ptr_factory_.GetWeakPtr()),
      base::Bind(&MobileSetupHandler::GetPropertiesFailure,
                 weak_ptr_factory_.GetWeakPtr(), network->path(),
                 kJsGetDeviceInfoCallback));
}

void MobileSetupHandler::GetPropertiesAndCallGetDeviceInfo(
    const std::string& service_path,
    const base::DictionaryValue& properties) {
  base::DictionaryValue device_info;
  GetDeviceInfo(properties, &device_info);
  web_ui()->CallJavascriptFunctionUnsafe(kJsGetDeviceInfoCallback, device_info);
}

void MobileSetupHandler::GetPropertiesFailure(
    const std::string& service_path,
    const std::string& callback_name,
    const std::string& error_name,
    std::unique_ptr<base::DictionaryValue> error_data) {
  NET_LOG_ERROR("MobileActivator GetProperties Failed: " + error_name,
                service_path);
  // Invoke |callback_name| with an empty dictionary.
  base::DictionaryValue device_dict;
  web_ui()->CallJavascriptFunctionUnsafe(callback_name, device_dict);
}

void MobileSetupHandler::DefaultNetworkChanged(
    const NetworkState* default_network) {
  if (!web_ui())
    return;

  std::string path = web_ui()->GetWebContents()->GetURL().path().substr(1);
  if (path.empty())
    return;

  const NetworkState* network =
      NetworkHandler::Get()->network_state_handler()->GetNetworkState(path);
  if (!network) {
    LOG(ERROR) << "Service path lost";
    web_ui()->GetWebContents()->Close();
    return;
  }

  UpdatePortalReachability(network, false /* do not force notification */);
}

void MobileSetupHandler::NetworkConnectionStateChanged(
    const NetworkState* network) {
  if (!web_ui())
    return;

  std::string path = web_ui()->GetWebContents()->GetURL().path().substr(1);
  if (path.empty() || path != network->path())
    return;

  UpdatePortalReachability(network, false /* do not force notification */);
}

void MobileSetupHandler::UpdatePortalReachability(
    const NetworkState* network,
    bool force_notification) {
  DCHECK(web_ui());

  DCHECK_EQ(type_, TYPE_PORTAL_LTE);

  chromeos::NetworkStateHandler* nsh =
      NetworkHandler::Get()->network_state_handler();
  bool portal_reachable =
      (network->IsConnectedState() ||
       (nsh->DefaultNetwork() &&
        nsh->DefaultNetwork()->connection_state() == shill::kStateOnline));

  if (force_notification || portal_reachable != lte_portal_reachable_) {
    web_ui()->CallJavascriptFunctionUnsafe(kJsConnectivityChangedCallback,
                                           base::Value(portal_reachable));
  }

  lte_portal_reachable_ = portal_reachable;
}

////////////////////////////////////////////////////////////////////////////////
//
// MobileSetupUI
//
////////////////////////////////////////////////////////////////////////////////

MobileSetupUI::MobileSetupUI(content::WebUI* web_ui)
    : WebUIController(web_ui) {
  web_ui->AddMessageHandler(std::make_unique<MobileSetupHandler>());
  MobileSetupUIHTMLSource* html_source = new MobileSetupUIHTMLSource();

  // Set up the chrome://mobilesetup/ source.
  Profile* profile = Profile::FromWebUI(web_ui);
  content::URLDataSource::Add(profile, html_source);

  content::WebContentsObserver::Observe(web_ui->GetWebContents());
}

void MobileSetupUI::DidFinishNavigation(
    content::NavigationHandle* navigation_handle) {
  if (!navigation_handle->HasCommitted() ||
      navigation_handle->GetRenderFrameHost()->GetFrameName() !=
          "paymentForm") {
    return;
  }

  if (navigation_handle->IsErrorPage()) {
    base::Value result_value(-navigation_handle->GetNetErrorCode());
    web_ui()->CallJavascriptFunctionUnsafe(kJsPortalFrameLoadFailedCallback,
                                           result_value);
    return;
  }

  web_ui()->CallJavascriptFunctionUnsafe(kJsPortalFrameLoadCompletedCallback);
}
