<commit_before>#include "vortex_controller/controller_ros.h"

#include "vortex/eigen_helper.h"
#include <tf/transform_datatypes.h>
#include <eigen_conversions/eigen_msg.h>

#include "std_msgs/String.h"

#include <math.h>
#include <map>
#include <string>
#include <vector>

Controller::Controller(ros::NodeHandle nh) : nh(nh)
{
  command_sub = nh.subscribe("propulsion_command", 10, &Controller::commandCallback, this);
  state_sub   = nh.subscribe("state_estimate", 10, &Controller::stateCallback, this);
  wrench_pub  = nh.advertise<geometry_msgs::Wrench>("rov_forces", 10);
  mode_pub    = nh.advertise<std_msgs::String>("controller/mode", 10);

  control_mode = ControlModes::OPEN_LOOP;

  if (!nh.getParam("/controller/frequency", frequency))
  {
    ROS_WARN("Failed to read parameter controller frequency, defaulting to 10 Hz.");
    frequency = 10;
  }

  state = new State();
  initSetpoints();
  initPositionHoldController();

  // Set up a dynamic reconfigure server
  dynamic_reconfigure::Server<vortex_controller::VortexControllerConfig>::CallbackType dr_cb;
  dr_cb = boost::bind(&Controller::configCallback, this, _1, _2);
  dr_srv.setCallback(dr_cb);

  ROS_INFO("Initialized.");
}

void Controller::commandCallback(const vortex_msgs::PropulsionCommand& msg)
{
  if (!healthyMessage(msg))
    return;

  ControlMode new_control_mode;
  new_control_mode = control_mode;
  for (int i = 0; i < msg.control_mode.size(); ++i)
  {
    if (msg.control_mode[i])
    {
      new_control_mode = static_cast<ControlMode>(i);
      break;
    }
  }

  if (new_control_mode != control_mode)
  {
    control_mode = new_control_mode;

    Eigen::Vector3d    position;
    Eigen::Quaterniond orientation;
    Eigen::Vector6d    velocity;

    // Reset setpoints to be equal to state
    state->get(&position, &orientation, &velocity);
    setpoints->set(position, orientation);

    ROS_INFO_STREAM("Changing mode to " << controlModeString(control_mode) << ".");
  }
  publishControlMode();

  double time = msg.header.stamp.toSec();
  Eigen::Vector6d command;
  for (int i = 0; i < 6; ++i)
    command(i) = msg.motion[i];
  setpoints->update(time, command);
}

void Controller::stateCallback(const nav_msgs::Odometry &msg)
{
  Eigen::Vector3d    position;
  Eigen::Quaterniond orientation;
  Eigen::Vector6d    velocity;

  tf::pointMsgToEigen(msg.pose.pose.position, position);
  tf::quaternionMsgToEigen(msg.pose.pose.orientation, orientation);
  tf::twistMsgToEigen(msg.twist.twist, velocity);

  bool orientation_invalid = (abs(orientation.norm() - 1) > MAX_QUAT_NORM_DEVIATION);
  if (isFucked(position) || isFucked(velocity) || orientation_invalid)
  {
    ROS_WARN_THROTTLE(1, "Invalid state estimate received, ignoring...");
    return;
  }

  state->set(position, orientation, velocity);
}

void Controller::configCallback(const vortex_controller::VortexControllerConfig &config, uint32_t level)
{
  ROS_INFO_STREAM("Setting gains: [vel = " << config.velocity_gain << ", pos = " << config.position_gain
    << ", rot = " << config.attitude_gain << "]");
  controller->setGains(config.velocity_gain, config.position_gain, config.attitude_gain);
}

void Controller::spin()
{
  Eigen::Vector6d    tau_command          = Eigen::VectorXd::Zero(6);
  Eigen::Vector6d    tau_openloop         = Eigen::VectorXd::Zero(6);
  Eigen::Vector6d    tau_restoring        = Eigen::VectorXd::Zero(6);
  Eigen::Vector6d    tau_staylevel        = Eigen::VectorXd::Zero(6);
  Eigen::Vector6d    tau_depthhold        = Eigen::VectorXd::Zero(6);
  Eigen::Vector6d    tau_headinghold      = Eigen::VectorXd::Zero(6);

  Eigen::Vector3d    position_state       = Eigen::Vector3d::Zero();
  Eigen::Quaterniond orientation_state    = Eigen::Quaterniond::Identity();
  Eigen::Vector6d    velocity_state       = Eigen::VectorXd::Zero(6);

  Eigen::Vector3d    position_setpoint    = Eigen::Vector3d::Zero();
  Eigen::Quaterniond orientation_setpoint = Eigen::Quaterniond::Identity();

  geometry_msgs::Wrench msg;

  ros::Rate rate(frequency);
  while (ros::ok())
  {
    // TODO(mortenfyhn): check value of bool return from getters

    // Read state and setpoint
    state->get(&position_state, &orientation_state, &velocity_state);
    setpoints->get(&position_setpoint, &orientation_setpoint);

    // Calculate terms of control vector
    setpoints->get(&tau_openloop);
    tau_restoring = controller->getRestoring(orientation_state);
    tau_staylevel = stayLevel(orientation_state, velocity_state);
    tau_depthhold = depthHold(tau_openloop, position_state, orientation_state, velocity_state, position_setpoint);
    tau_headinghold = headingHold(tau_openloop, position_state, orientation_state, velocity_state, orientation_setpoint);

    switch (control_mode)
    {
      case ControlModes::OPEN_LOOP:
      tau_command = tau_openloop;
      break;

      case ControlModes::OPEN_LOOP_RESTORING:
      tau_command = tau_openloop + tau_restoring;
      break;

      case ControlModes::STAY_LEVEL:
      tau_command = tau_openloop + tau_staylevel;
      break;

      case ControlModes::DEPTH_HOLD:
      tau_command = tau_openloop + tau_depthhold;
      break;

      case ControlModes::HEADING_HOLD:
      tau_command = tau_openloop + tau_headinghold;
      break;

      case ControlModes::DEPTH_HEADING_HOLD:
      tau_command = tau_openloop + tau_depthhold + tau_headinghold;
      break;

      default:
      ROS_ERROR("Default control mode reached.");
      break;
    }

    tf::wrenchEigenToMsg(tau_command, msg);
    wrench_pub.publish(msg);

    ros::spinOnce();
    rate.sleep();
  }
}

void Controller::initSetpoints()
{
  std::vector<double> v;

  if (!nh.getParam("/propulsion/command/wrench/max", v))
    ROS_FATAL("Failed to read parameter max wrench command.");
  Eigen::Vector6d wrench_command_max = Eigen::Vector6d::Map(v.data(), v.size());

  if (!nh.getParam("/propulsion/command/wrench/scaling", v))
    ROS_FATAL("Failed to read parameter scaling wrench command.");
  Eigen::Vector6d wrench_command_scaling = Eigen::Vector6d::Map(v.data(), v.size());

  if (!nh.getParam("/propulsion/command/pose/rate", v))
    ROS_FATAL("Failed to read parameter pose command rate.");
  Eigen::Vector6d pose_command_rate = Eigen::Vector6d::Map(v.data(), v.size());

  setpoints = new Setpoints(wrench_command_scaling,
                            wrench_command_max,
                            pose_command_rate);
}

void Controller::initPositionHoldController()
{
  // Read controller gains from parameter server
  double a, b, c;
  if (!nh.getParam("/controller/velocity_gain", a))
    ROS_ERROR("Failed to read parameter velocity_gain.");
  if (!nh.getParam("/controller/position_gain", b))
    ROS_ERROR("Failed to read parameter position_gain.");
  if (!nh.getParam("/controller/attitude_gain", c))
    ROS_ERROR("Failed to read parameter attitude_gain.");

  // Read center of gravity and buoyancy vectors
  std::vector<double> r_G_vec, r_B_vec;
  if (!nh.getParam("/physical/center_of_mass", r_G_vec))
    ROS_FATAL("Failed to read robot center of mass parameter.");
  if (!nh.getParam("/physical/center_of_buoyancy", r_B_vec))
    ROS_FATAL("Failed to read robot center of buoyancy parameter.");
  Eigen::Vector3d r_G(r_G_vec.data());
  Eigen::Vector3d r_B(r_B_vec.data());

  // Read and calculate ROV weight and buoyancy
  double mass, displacement, acceleration_of_gravity, density_of_water;
  if (!nh.getParam("/physical/mass_kg", mass))
    ROS_FATAL("Failed to read parameter mass.");
  if (!nh.getParam("/physical/displacement_m3", displacement))
    ROS_FATAL("Failed to read parameter displacement.");
  if (!nh.getParam("/gravity/acceleration", acceleration_of_gravity))
    ROS_FATAL("Failed to read parameter acceleration of gravity");
  if (!nh.getParam("/water/density", density_of_water))
    ROS_FATAL("Failed to read parameter density of water");
  double W = mass * acceleration_of_gravity;
  double B = density_of_water * displacement * acceleration_of_gravity;

  controller = new QuaternionPdController(a, b, c, W, B, r_G, r_B);
}

bool Controller::healthyMessage(const vortex_msgs::PropulsionCommand& msg)
{
  // Check that motion commands are in range
  for (int i = 0; i < msg.motion.size(); ++i)
  {
    if (msg.motion[i] > 1 || msg.motion[i] < -1)
    {
      ROS_WARN("Motion command out of range, ignoring message...");
      return false;
    }
  }

  // Check correct length of control mode vector
  if (msg.control_mode.size() != ControlModes::CONTROL_MODE_END)
  {
    ROS_WARN_STREAM_THROTTLE(1, "Control mode vector has " << msg.control_mode.size()
      << " element(s), should have " << ControlModes::CONTROL_MODE_END);
    return false;
  }

  // Check that exactly zero or one control mode is requested
  int num_requested_modes = 0;
  for (int i = 0; i < msg.control_mode.size(); ++i)
    if (msg.control_mode[i])
      num_requested_modes++;
  if (num_requested_modes > 1)
  {
    ROS_WARN_STREAM("Attempt to set " << num_requested_modes << " control modes at once, ignoring message...");
    return false;
  }

  return true;
}

void Controller::publishControlMode()
{
  std::string s = controlModeString(control_mode);
  std_msgs::String msg;
  msg.data = s;
  mode_pub.publish(msg);
}

Eigen::Vector6d Controller::stayLevel(const Eigen::Quaterniond &orientation_state,
                                      const Eigen::Vector6d &velocity_state)
{
  // Convert quaternion setpoint to euler angles (ZYX convention)
  Eigen::Vector3d euler;
  euler = orientation_state.toRotationMatrix().eulerAngles(2, 1, 0);

  // Set pitch and roll setpoints to zero
  euler(EULER_PITCH) = 0;
  euler(EULER_ROLL)  = 0;

  // Convert euler setpoint back to quaternions
  Eigen::Matrix3d R;
  R = Eigen::AngleAxisd(euler(EULER_YAW),   Eigen::Vector3d::UnitZ())
    * Eigen::AngleAxisd(euler(EULER_PITCH), Eigen::Vector3d::UnitY())
    * Eigen::AngleAxisd(euler(EULER_ROLL),  Eigen::Vector3d::UnitX());
  Eigen::Quaterniond orientation_staylevel(R);

  Eigen::Vector6d tau = controller->getFeedback(Eigen::Vector3d::Zero(), orientation_state, velocity_state,
                                                Eigen::Vector3d::Zero(), orientation_staylevel);

  tau(WRENCH_ROLL)  = 0;
  tau(WRENCH_PITCH) = 0;

  return tau;
}

Eigen::Vector6d Controller::depthHold(const Eigen::Vector6d &tau_openloop,
                                      const Eigen::Vector3d &position_state,
                                      const Eigen::Quaterniond &orientation_state,
                                      const Eigen::Vector6d &velocity_state,
                                      const Eigen::Vector3d &position_setpoint)
{
  Eigen::Vector6d tau;

  bool activate_depthhold = fabs(tau_openloop(WRENCH_HEAVE)) < FORCE_DEADZONE_LIMIT;
  if (activate_depthhold)
  {
    tau = controller->getFeedback(position_state, Eigen::Quaterniond::Identity(), velocity_state,
                                  position_setpoint, Eigen::Quaterniond::Identity());

    // Allow only heave feedback command
    tau(WRENCH_SURGE) = 0;
    tau(WRENCH_SWAY)  = 0;
    tau(WRENCH_ROLL)  = 0;
    tau(WRENCH_PITCH) = 0;
    tau(WRENCH_YAW)   = 0;
  }
  else
  {
    setpoints->set(position_state, orientation_state);
    tau.setZero();
  }

  return tau;
}

Eigen::Vector6d Controller::headingHold(const Eigen::Vector6d &tau_openloop,
                                        const Eigen::Vector3d &position_state,
                                        const Eigen::Quaterniond &orientation_state,
                                        const Eigen::Vector6d &velocity_state,
                                        const Eigen::Quaterniond &orientation_setpoint)
{
  Eigen::Vector6d tau;

  bool activate_headinghold = fabs(tau_openloop(WRENCH_YAW)) < FORCE_DEADZONE_LIMIT;
  if (activate_headinghold)
  {
    tau = controller->getFeedback(Eigen::Vector3d::Zero(), orientation_state, velocity_state,
                                  Eigen::Vector3d::Zero(), orientation_setpoint);

    // Allow only yaw feedback command
    tau(WRENCH_SURGE) = 0;
    tau(WRENCH_SWAY)  = 0;
    tau(WRENCH_HEAVE) = 0;
    tau(WRENCH_ROLL)  = 0;
    tau(WRENCH_PITCH) = 0;
  }
  else
  {
    setpoints->set(position_state, orientation_state);
    tau.setZero();
  }

  return tau;
}
<commit_msg>Set control command to zero when starting control loop<commit_after>#include "vortex_controller/controller_ros.h"

#include "vortex/eigen_helper.h"
#include <tf/transform_datatypes.h>
#include <eigen_conversions/eigen_msg.h>

#include "std_msgs/String.h"

#include <math.h>
#include <map>
#include <string>
#include <vector>

Controller::Controller(ros::NodeHandle nh) : nh(nh)
{
  command_sub = nh.subscribe("propulsion_command", 10, &Controller::commandCallback, this);
  state_sub   = nh.subscribe("state_estimate", 10, &Controller::stateCallback, this);
  wrench_pub  = nh.advertise<geometry_msgs::Wrench>("rov_forces", 10);
  mode_pub    = nh.advertise<std_msgs::String>("controller/mode", 10);

  control_mode = ControlModes::OPEN_LOOP;

  if (!nh.getParam("/controller/frequency", frequency))
  {
    ROS_WARN("Failed to read parameter controller frequency, defaulting to 10 Hz.");
    frequency = 10;
  }

  state = new State();
  initSetpoints();
  initPositionHoldController();

  // Set up a dynamic reconfigure server
  dynamic_reconfigure::Server<vortex_controller::VortexControllerConfig>::CallbackType dr_cb;
  dr_cb = boost::bind(&Controller::configCallback, this, _1, _2);
  dr_srv.setCallback(dr_cb);

  ROS_INFO("Initialized.");
}

void Controller::commandCallback(const vortex_msgs::PropulsionCommand& msg)
{
  if (!healthyMessage(msg))
    return;

  ControlMode new_control_mode;
  new_control_mode = control_mode;
  for (int i = 0; i < msg.control_mode.size(); ++i)
  {
    if (msg.control_mode[i])
    {
      new_control_mode = static_cast<ControlMode>(i);
      break;
    }
  }

  if (new_control_mode != control_mode)
  {
    control_mode = new_control_mode;

    Eigen::Vector3d    position;
    Eigen::Quaterniond orientation;
    Eigen::Vector6d    velocity;

    // Reset setpoints to be equal to state
    state->get(&position, &orientation, &velocity);
    setpoints->set(position, orientation);

    ROS_INFO_STREAM("Changing mode to " << controlModeString(control_mode) << ".");
  }
  publishControlMode();

  double time = msg.header.stamp.toSec();
  Eigen::Vector6d command;
  for (int i = 0; i < 6; ++i)
    command(i) = msg.motion[i];
  setpoints->update(time, command);
}

void Controller::stateCallback(const nav_msgs::Odometry &msg)
{
  Eigen::Vector3d    position;
  Eigen::Quaterniond orientation;
  Eigen::Vector6d    velocity;

  tf::pointMsgToEigen(msg.pose.pose.position, position);
  tf::quaternionMsgToEigen(msg.pose.pose.orientation, orientation);
  tf::twistMsgToEigen(msg.twist.twist, velocity);

  bool orientation_invalid = (abs(orientation.norm() - 1) > MAX_QUAT_NORM_DEVIATION);
  if (isFucked(position) || isFucked(velocity) || orientation_invalid)
  {
    ROS_WARN_THROTTLE(1, "Invalid state estimate received, ignoring...");
    return;
  }

  state->set(position, orientation, velocity);
}

void Controller::configCallback(const vortex_controller::VortexControllerConfig &config, uint32_t level)
{
  ROS_INFO_STREAM("Setting gains: [vel = " << config.velocity_gain << ", pos = " << config.position_gain
    << ", rot = " << config.attitude_gain << "]");
  controller->setGains(config.velocity_gain, config.position_gain, config.attitude_gain);
}

void Controller::spin()
{
  Eigen::Vector6d    tau_command          = Eigen::VectorXd::Zero(6);
  Eigen::Vector6d    tau_openloop         = Eigen::VectorXd::Zero(6);
  Eigen::Vector6d    tau_restoring        = Eigen::VectorXd::Zero(6);
  Eigen::Vector6d    tau_staylevel        = Eigen::VectorXd::Zero(6);
  Eigen::Vector6d    tau_depthhold        = Eigen::VectorXd::Zero(6);
  Eigen::Vector6d    tau_headinghold      = Eigen::VectorXd::Zero(6);

  Eigen::Vector3d    position_state       = Eigen::Vector3d::Zero();
  Eigen::Quaterniond orientation_state    = Eigen::Quaterniond::Identity();
  Eigen::Vector6d    velocity_state       = Eigen::VectorXd::Zero(6);

  Eigen::Vector3d    position_setpoint    = Eigen::Vector3d::Zero();
  Eigen::Quaterniond orientation_setpoint = Eigen::Quaterniond::Identity();

  geometry_msgs::Wrench msg;

  ros::Rate rate(frequency);
  while (ros::ok())
  {
    // TODO(mortenfyhn): check value of bool return from getters

    // Read state and setpoint
    state->get(&position_state, &orientation_state, &velocity_state);
    setpoints->get(&position_setpoint, &orientation_setpoint);

    // Calculate terms of control vector
    setpoints->get(&tau_openloop);
    tau_restoring = controller->getRestoring(orientation_state);
    tau_staylevel = stayLevel(orientation_state, velocity_state);
    tau_depthhold = depthHold(tau_openloop, position_state, orientation_state, velocity_state, position_setpoint);
    tau_headinghold = headingHold(tau_openloop, position_state, orientation_state, velocity_state, orientation_setpoint);
    tau_command.setZero();

    switch (control_mode)
    {
      case ControlModes::OPEN_LOOP:
      tau_command = tau_openloop;
      break;

      case ControlModes::OPEN_LOOP_RESTORING:
      tau_command = tau_openloop + tau_restoring;
      break;

      case ControlModes::STAY_LEVEL:
      tau_command = tau_openloop + tau_staylevel;
      break;

      case ControlModes::DEPTH_HOLD:
      tau_command = tau_openloop + tau_depthhold;
      break;

      case ControlModes::HEADING_HOLD:
      tau_command = tau_openloop + tau_headinghold;
      break;

      case ControlModes::DEPTH_HEADING_HOLD:
      tau_command = tau_openloop + tau_depthhold + tau_headinghold;
      break;

      default:
      ROS_ERROR("Default control mode reached.");
      break;
    }

    tf::wrenchEigenToMsg(tau_command, msg);
    wrench_pub.publish(msg);

    ros::spinOnce();
    rate.sleep();
  }
}

void Controller::initSetpoints()
{
  std::vector<double> v;

  if (!nh.getParam("/propulsion/command/wrench/max", v))
    ROS_FATAL("Failed to read parameter max wrench command.");
  Eigen::Vector6d wrench_command_max = Eigen::Vector6d::Map(v.data(), v.size());

  if (!nh.getParam("/propulsion/command/wrench/scaling", v))
    ROS_FATAL("Failed to read parameter scaling wrench command.");
  Eigen::Vector6d wrench_command_scaling = Eigen::Vector6d::Map(v.data(), v.size());

  if (!nh.getParam("/propulsion/command/pose/rate", v))
    ROS_FATAL("Failed to read parameter pose command rate.");
  Eigen::Vector6d pose_command_rate = Eigen::Vector6d::Map(v.data(), v.size());

  setpoints = new Setpoints(wrench_command_scaling,
                            wrench_command_max,
                            pose_command_rate);
}

void Controller::initPositionHoldController()
{
  // Read controller gains from parameter server
  double a, b, c;
  if (!nh.getParam("/controller/velocity_gain", a))
    ROS_ERROR("Failed to read parameter velocity_gain.");
  if (!nh.getParam("/controller/position_gain", b))
    ROS_ERROR("Failed to read parameter position_gain.");
  if (!nh.getParam("/controller/attitude_gain", c))
    ROS_ERROR("Failed to read parameter attitude_gain.");

  // Read center of gravity and buoyancy vectors
  std::vector<double> r_G_vec, r_B_vec;
  if (!nh.getParam("/physical/center_of_mass", r_G_vec))
    ROS_FATAL("Failed to read robot center of mass parameter.");
  if (!nh.getParam("/physical/center_of_buoyancy", r_B_vec))
    ROS_FATAL("Failed to read robot center of buoyancy parameter.");
  Eigen::Vector3d r_G(r_G_vec.data());
  Eigen::Vector3d r_B(r_B_vec.data());

  // Read and calculate ROV weight and buoyancy
  double mass, displacement, acceleration_of_gravity, density_of_water;
  if (!nh.getParam("/physical/mass_kg", mass))
    ROS_FATAL("Failed to read parameter mass.");
  if (!nh.getParam("/physical/displacement_m3", displacement))
    ROS_FATAL("Failed to read parameter displacement.");
  if (!nh.getParam("/gravity/acceleration", acceleration_of_gravity))
    ROS_FATAL("Failed to read parameter acceleration of gravity");
  if (!nh.getParam("/water/density", density_of_water))
    ROS_FATAL("Failed to read parameter density of water");
  double W = mass * acceleration_of_gravity;
  double B = density_of_water * displacement * acceleration_of_gravity;

  controller = new QuaternionPdController(a, b, c, W, B, r_G, r_B);
}

bool Controller::healthyMessage(const vortex_msgs::PropulsionCommand& msg)
{
  // Check that motion commands are in range
  for (int i = 0; i < msg.motion.size(); ++i)
  {
    if (msg.motion[i] > 1 || msg.motion[i] < -1)
    {
      ROS_WARN("Motion command out of range, ignoring message...");
      return false;
    }
  }

  // Check correct length of control mode vector
  if (msg.control_mode.size() != ControlModes::CONTROL_MODE_END)
  {
    ROS_WARN_STREAM_THROTTLE(1, "Control mode vector has " << msg.control_mode.size()
      << " element(s), should have " << ControlModes::CONTROL_MODE_END);
    return false;
  }

  // Check that exactly zero or one control mode is requested
  int num_requested_modes = 0;
  for (int i = 0; i < msg.control_mode.size(); ++i)
    if (msg.control_mode[i])
      num_requested_modes++;
  if (num_requested_modes > 1)
  {
    ROS_WARN_STREAM("Attempt to set " << num_requested_modes << " control modes at once, ignoring message...");
    return false;
  }

  return true;
}

void Controller::publishControlMode()
{
  std::string s = controlModeString(control_mode);
  std_msgs::String msg;
  msg.data = s;
  mode_pub.publish(msg);
}

Eigen::Vector6d Controller::stayLevel(const Eigen::Quaterniond &orientation_state,
                                      const Eigen::Vector6d &velocity_state)
{
  // Convert quaternion setpoint to euler angles (ZYX convention)
  Eigen::Vector3d euler;
  euler = orientation_state.toRotationMatrix().eulerAngles(2, 1, 0);

  // Set pitch and roll setpoints to zero
  euler(EULER_PITCH) = 0;
  euler(EULER_ROLL)  = 0;

  // Convert euler setpoint back to quaternions
  Eigen::Matrix3d R;
  R = Eigen::AngleAxisd(euler(EULER_YAW),   Eigen::Vector3d::UnitZ())
    * Eigen::AngleAxisd(euler(EULER_PITCH), Eigen::Vector3d::UnitY())
    * Eigen::AngleAxisd(euler(EULER_ROLL),  Eigen::Vector3d::UnitX());
  Eigen::Quaterniond orientation_staylevel(R);

  Eigen::Vector6d tau = controller->getFeedback(Eigen::Vector3d::Zero(), orientation_state, velocity_state,
                                                Eigen::Vector3d::Zero(), orientation_staylevel);

  tau(WRENCH_ROLL)  = 0;
  tau(WRENCH_PITCH) = 0;

  return tau;
}

Eigen::Vector6d Controller::depthHold(const Eigen::Vector6d &tau_openloop,
                                      const Eigen::Vector3d &position_state,
                                      const Eigen::Quaterniond &orientation_state,
                                      const Eigen::Vector6d &velocity_state,
                                      const Eigen::Vector3d &position_setpoint)
{
  Eigen::Vector6d tau;

  bool activate_depthhold = fabs(tau_openloop(WRENCH_HEAVE)) < FORCE_DEADZONE_LIMIT;
  if (activate_depthhold)
  {
    tau = controller->getFeedback(position_state, Eigen::Quaterniond::Identity(), velocity_state,
                                  position_setpoint, Eigen::Quaterniond::Identity());

    // Allow only heave feedback command
    tau(WRENCH_SURGE) = 0;
    tau(WRENCH_SWAY)  = 0;
    tau(WRENCH_ROLL)  = 0;
    tau(WRENCH_PITCH) = 0;
    tau(WRENCH_YAW)   = 0;
  }
  else
  {
    setpoints->set(position_state, orientation_state);
    tau.setZero();
  }

  return tau;
}

Eigen::Vector6d Controller::headingHold(const Eigen::Vector6d &tau_openloop,
                                        const Eigen::Vector3d &position_state,
                                        const Eigen::Quaterniond &orientation_state,
                                        const Eigen::Vector6d &velocity_state,
                                        const Eigen::Quaterniond &orientation_setpoint)
{
  Eigen::Vector6d tau;

  bool activate_headinghold = fabs(tau_openloop(WRENCH_YAW)) < FORCE_DEADZONE_LIMIT;
  if (activate_headinghold)
  {
    tau = controller->getFeedback(Eigen::Vector3d::Zero(), orientation_state, velocity_state,
                                  Eigen::Vector3d::Zero(), orientation_setpoint);

    // Allow only yaw feedback command
    tau(WRENCH_SURGE) = 0;
    tau(WRENCH_SWAY)  = 0;
    tau(WRENCH_HEAVE) = 0;
    tau(WRENCH_ROLL)  = 0;
    tau(WRENCH_PITCH) = 0;
  }
  else
  {
    setpoints->set(position_state, orientation_state);
    tau.setZero();
  }

  return tau;
}
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 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.
//
// This job type handles Chrome plugin network interception.  When a plugin
// wants to intercept a request, a job of this type is created.  The intercept
// job communicates with the plugin to retrieve the response headers and data.

#include "chrome/common/net/url_request_intercept_job.h"

#include "base/message_loop.h"
#include "base/string_util.h"
#include "chrome/common/chrome_plugin_lib.h"
#include "chrome/common/notification_service.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/x509_certificate.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request.h"

using base::Time;
using base::TimeDelta;

//
// URLRequestInterceptJob
//

URLRequestInterceptJob::URLRequestInterceptJob(URLRequest* request,
                                               ChromePluginLib* plugin,
                                               ScopableCPRequest* cprequest)
    : URLRequestJob(request),
      cprequest_(cprequest),
      plugin_(plugin),
      read_buffer_(NULL) {
  cprequest_->data = this;  // see FromCPRequest().

  registrar_.Add(this, NotificationType::CHROME_PLUGIN_UNLOADED,
                 Source<ChromePluginLib>(plugin_));
}

URLRequestInterceptJob::~URLRequestInterceptJob() {
  if (plugin_) {
    plugin_->functions().request_funcs->end_request(cprequest_.get(),
                                                    CPERR_SUCCESS);
  }
}

void URLRequestInterceptJob::DetachPlugin() {
  registrar_.RemoveAll();
  plugin_ = NULL;
}

void URLRequestInterceptJob::Start() {
  // Start reading asynchronously so that all error reporting and data
  // callbacks happen as they would for network requests.
  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
      this, &URLRequestInterceptJob::StartAsync));
}

void URLRequestInterceptJob::Kill() {
  if (plugin_) {
    plugin_->functions().request_funcs->end_request(cprequest_.get(),
                                                    CPERR_CANCELLED);
    DetachPlugin();
  }
  URLRequestJob::Kill();
}

bool URLRequestInterceptJob::ReadRawData(net::IOBuffer* dest, int dest_size,
                                         int* bytes_read) {
  DCHECK_NE(dest_size, 0);
  DCHECK(bytes_read);

  if (!plugin_)
    return false;

  int rv = plugin_->functions().request_funcs->read(cprequest_.get(),
                                                    dest->data(), dest_size);
  if (rv >= 0) {
    *bytes_read = rv;
    return true;
  }

  if (rv == CPERR_IO_PENDING) {
    read_buffer_ = dest;
    read_buffer_size_ = dest_size;
    SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
  } else {
    // TODO(mpcomplete): better error code
    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, net::ERR_FAILED));
  }

  return false;
}

bool URLRequestInterceptJob::GetMimeType(std::string* mime_type) const {
  return request_->response_headers()->GetMimeType(mime_type);
}

bool URLRequestInterceptJob::GetCharset(std::string* charset) {
  return request_->response_headers()->GetCharset(charset);
}

bool URLRequestInterceptJob::GetContentEncodings(
    std::vector<Filter::FilterType>* encoding_types) {
  DCHECK(encoding_types->empty());
  if (!request_->response_headers())
    return false;

  std::string encoding_type;
  void* iter = NULL;
  while (request_->response_headers()->EnumerateHeader(
      &iter, "Content-Encoding", &encoding_type)) {
    encoding_types->push_back(Filter::ConvertEncodingToType(encoding_type));
  }

  // Even if encoding types are empty, there is a chance that we need to add
  // some decoding, as some proxies strip encoding completely. In such cases,
  // we may need to add (for example) SDCH filtering (when the context suggests
  // it is appropriate).
  Filter::FixupEncodingTypes(*this, encoding_types);
  return !encoding_types->empty();
}

void URLRequestInterceptJob::GetResponseInfo(net::HttpResponseInfo* info) {
  if (!plugin_)
    return;

  std::string raw_headers;
  int size = plugin_->functions().request_funcs->get_response_info(
      cprequest_.get(), CPRESPONSEINFO_HTTP_RAW_HEADERS, NULL, 0);
  int rv = size < 0 ? size :
      plugin_->functions().request_funcs->get_response_info(
          cprequest_.get(), CPRESPONSEINFO_HTTP_RAW_HEADERS,
          WriteInto(&raw_headers, size+1), size);
  if (rv != CPERR_SUCCESS) {
    // TODO(mpcomplete): what should we do here?
    raw_headers = "HTTP/1.1 404 Not Found" + '\0';
  }

  info->headers = new net::HttpResponseHeaders(raw_headers);

  if (request_->url().SchemeIsSecure()) {
    // Make up a fake certificate for intercepted data since we don't have
    // access to the real SSL info.
    // TODO(mpcomplete): we should probably have the interception API transmit
    // the SSL info, but the only consumer of this API (Gears) doesn't keep that
    // around. We should change that.
    const char* kCertIssuer = "Chrome Internal";
    const int kLifetimeDays = 100;

    DLOG(WARNING) << "Issuing a fake SSL certificate for interception of URL "
        << request_->url();

    info->ssl_info.cert =
        new net::X509Certificate(request_->url().GetWithEmptyPath().spec(),
                                 kCertIssuer,
                                 Time::Now(),
                                 Time::Now() +
                                     TimeDelta::FromDays(kLifetimeDays));
    info->ssl_info.cert_status = 0;
    info->ssl_info.security_bits = 0;
  }
}

int URLRequestInterceptJob::GetResponseCode() const {
  if (!plugin_)
    return -1;

  int status = 200;
  plugin_->functions().request_funcs->get_response_info(
      cprequest_.get(), CPRESPONSEINFO_HTTP_STATUS, &status, sizeof(status));

  return status;
}

bool URLRequestInterceptJob::IsRedirectResponse(GURL* location,
                                                int* http_status_code) {
  if (!request_->response_headers())
    return false;

  std::string value;
  if (!request_->response_headers()->IsRedirect(&value))
    return false;

  *location = request_->url().Resolve(value);
  *http_status_code = request_->response_headers()->response_code();
  return true;
}

void URLRequestInterceptJob::StartAsync() {
  // We may have been orphaned...
  if (!request_ || !plugin_)
    return;

  int rv = plugin_->functions().request_funcs->start_request(cprequest_.get());
  if (rv != CPERR_IO_PENDING)
    OnStartCompleted(rv);
}

void URLRequestInterceptJob::OnStartCompleted(int result) {
  if (result != CPERR_SUCCESS) {
    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
                                net::ERR_CONNECTION_FAILED));
    return;
  }

  NotifyHeadersComplete();
}

void URLRequestInterceptJob::OnReadCompleted(int bytes_read) {
  if (bytes_read < 0) {
    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, net::ERR_FAILED));
    return;
  }

  SetStatus(URLRequestStatus());  // clear the async flag
  NotifyReadComplete(bytes_read);
}

void URLRequestInterceptJob::Observe(NotificationType type,
                                     const NotificationSource& source,
                                     const NotificationDetails& details) {
  DCHECK(type == NotificationType::CHROME_PLUGIN_UNLOADED);
  DCHECK(plugin_ == Source<ChromePluginLib>(source).ptr());
  DetachPlugin();
}
<commit_msg>Fix unintentional pointer arithmetic.<commit_after>// Copyright (c) 2006-2008 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.
//
// This job type handles Chrome plugin network interception.  When a plugin
// wants to intercept a request, a job of this type is created.  The intercept
// job communicates with the plugin to retrieve the response headers and data.

#include "chrome/common/net/url_request_intercept_job.h"

#include "base/message_loop.h"
#include "base/string_util.h"
#include "chrome/common/chrome_plugin_lib.h"
#include "chrome/common/notification_service.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/x509_certificate.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request.h"

using base::Time;
using base::TimeDelta;

//
// URLRequestInterceptJob
//

URLRequestInterceptJob::URLRequestInterceptJob(URLRequest* request,
                                               ChromePluginLib* plugin,
                                               ScopableCPRequest* cprequest)
    : URLRequestJob(request),
      cprequest_(cprequest),
      plugin_(plugin),
      read_buffer_(NULL) {
  cprequest_->data = this;  // see FromCPRequest().

  registrar_.Add(this, NotificationType::CHROME_PLUGIN_UNLOADED,
                 Source<ChromePluginLib>(plugin_));
}

URLRequestInterceptJob::~URLRequestInterceptJob() {
  if (plugin_) {
    plugin_->functions().request_funcs->end_request(cprequest_.get(),
                                                    CPERR_SUCCESS);
  }
}

void URLRequestInterceptJob::DetachPlugin() {
  registrar_.RemoveAll();
  plugin_ = NULL;
}

void URLRequestInterceptJob::Start() {
  // Start reading asynchronously so that all error reporting and data
  // callbacks happen as they would for network requests.
  MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
      this, &URLRequestInterceptJob::StartAsync));
}

void URLRequestInterceptJob::Kill() {
  if (plugin_) {
    plugin_->functions().request_funcs->end_request(cprequest_.get(),
                                                    CPERR_CANCELLED);
    DetachPlugin();
  }
  URLRequestJob::Kill();
}

bool URLRequestInterceptJob::ReadRawData(net::IOBuffer* dest, int dest_size,
                                         int* bytes_read) {
  DCHECK_NE(dest_size, 0);
  DCHECK(bytes_read);

  if (!plugin_)
    return false;

  int rv = plugin_->functions().request_funcs->read(cprequest_.get(),
                                                    dest->data(), dest_size);
  if (rv >= 0) {
    *bytes_read = rv;
    return true;
  }

  if (rv == CPERR_IO_PENDING) {
    read_buffer_ = dest;
    read_buffer_size_ = dest_size;
    SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0));
  } else {
    // TODO(mpcomplete): better error code
    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, net::ERR_FAILED));
  }

  return false;
}

bool URLRequestInterceptJob::GetMimeType(std::string* mime_type) const {
  return request_->response_headers()->GetMimeType(mime_type);
}

bool URLRequestInterceptJob::GetCharset(std::string* charset) {
  return request_->response_headers()->GetCharset(charset);
}

bool URLRequestInterceptJob::GetContentEncodings(
    std::vector<Filter::FilterType>* encoding_types) {
  DCHECK(encoding_types->empty());
  if (!request_->response_headers())
    return false;

  std::string encoding_type;
  void* iter = NULL;
  while (request_->response_headers()->EnumerateHeader(
      &iter, "Content-Encoding", &encoding_type)) {
    encoding_types->push_back(Filter::ConvertEncodingToType(encoding_type));
  }

  // Even if encoding types are empty, there is a chance that we need to add
  // some decoding, as some proxies strip encoding completely. In such cases,
  // we may need to add (for example) SDCH filtering (when the context suggests
  // it is appropriate).
  Filter::FixupEncodingTypes(*this, encoding_types);
  return !encoding_types->empty();
}

void URLRequestInterceptJob::GetResponseInfo(net::HttpResponseInfo* info) {
  if (!plugin_)
    return;

  std::string raw_headers;
  int size = plugin_->functions().request_funcs->get_response_info(
      cprequest_.get(), CPRESPONSEINFO_HTTP_RAW_HEADERS, NULL, 0);
  int rv = size < 0 ? size :
      plugin_->functions().request_funcs->get_response_info(
          cprequest_.get(), CPRESPONSEINFO_HTTP_RAW_HEADERS,
          WriteInto(&raw_headers, size+1), size);
  if (rv != CPERR_SUCCESS) {
    // TODO(mpcomplete): what should we do here?
    raw_headers = "HTTP/1.1 404 Not Found";
    raw_headers.push_back('\0');
  }

  info->headers = new net::HttpResponseHeaders(raw_headers);

  if (request_->url().SchemeIsSecure()) {
    // Make up a fake certificate for intercepted data since we don't have
    // access to the real SSL info.
    // TODO(mpcomplete): we should probably have the interception API transmit
    // the SSL info, but the only consumer of this API (Gears) doesn't keep that
    // around. We should change that.
    const char* kCertIssuer = "Chrome Internal";
    const int kLifetimeDays = 100;

    DLOG(WARNING) << "Issuing a fake SSL certificate for interception of URL "
        << request_->url();

    info->ssl_info.cert =
        new net::X509Certificate(request_->url().GetWithEmptyPath().spec(),
                                 kCertIssuer,
                                 Time::Now(),
                                 Time::Now() +
                                     TimeDelta::FromDays(kLifetimeDays));
    info->ssl_info.cert_status = 0;
    info->ssl_info.security_bits = 0;
  }
}

int URLRequestInterceptJob::GetResponseCode() const {
  if (!plugin_)
    return -1;

  int status = 200;
  plugin_->functions().request_funcs->get_response_info(
      cprequest_.get(), CPRESPONSEINFO_HTTP_STATUS, &status, sizeof(status));

  return status;
}

bool URLRequestInterceptJob::IsRedirectResponse(GURL* location,
                                                int* http_status_code) {
  if (!request_->response_headers())
    return false;

  std::string value;
  if (!request_->response_headers()->IsRedirect(&value))
    return false;

  *location = request_->url().Resolve(value);
  *http_status_code = request_->response_headers()->response_code();
  return true;
}

void URLRequestInterceptJob::StartAsync() {
  // We may have been orphaned...
  if (!request_ || !plugin_)
    return;

  int rv = plugin_->functions().request_funcs->start_request(cprequest_.get());
  if (rv != CPERR_IO_PENDING)
    OnStartCompleted(rv);
}

void URLRequestInterceptJob::OnStartCompleted(int result) {
  if (result != CPERR_SUCCESS) {
    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED,
                                net::ERR_CONNECTION_FAILED));
    return;
  }

  NotifyHeadersComplete();
}

void URLRequestInterceptJob::OnReadCompleted(int bytes_read) {
  if (bytes_read < 0) {
    NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, net::ERR_FAILED));
    return;
  }

  SetStatus(URLRequestStatus());  // clear the async flag
  NotifyReadComplete(bytes_read);
}

void URLRequestInterceptJob::Observe(NotificationType type,
                                     const NotificationSource& source,
                                     const NotificationDetails& details) {
  DCHECK(type == NotificationType::CHROME_PLUGIN_UNLOADED);
  DCHECK(plugin_ == Source<ChromePluginLib>(source).ptr());
  DetachPlugin();
}
<|endoftext|>


<commit_before>// Copyright (c) 2009 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 "webkit/glue/plugins/plugin_list.h"

#include "base/command_line.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/string_util.h"

namespace {

// Return true if we're in debug-plugin-loading mode.
bool DebugPluginLoading() {
  static const wchar_t kDebugPluginLoading[] = L"debug-plugin-loading";
  return CommandLine::ForCurrentProcess()->HasSwitch(kDebugPluginLoading);
}

// We build up a list of files and mtimes so we can sort them.
typedef std::pair<FilePath, base::Time> FileAndTime;
typedef std::vector<FileAndTime> FileTimeList;

// Comparator used to sort by descending mtime then ascending filename.
bool CompareTime(const FileAndTime& a, const FileAndTime& b) {
  if (a.second == b.second) {
    // Fall back on filename sorting, just to make the predicate valid.
    return a.first < b.first;
  }

  // Sort by mtime, descending.
  return a.second > b.second;
}

// Some plugins are shells around other plugins; we prefer to use the
// real plugin directly, if it's available.  This function returns
// true if we should prefer other plugins over this one.  We'll still
// use a "undesirable" plugin if no other option is available.
bool IsUndesirablePlugin(const WebPluginInfo& info) {
  std::string filename = info.path.BaseName().value();
  return (filename.find("npcxoffice") != std::string::npos || // Crossover
          filename.find("npwrapper") != std::string::npos);   // nspluginwrapper
}

}  // anonymous namespace

namespace NPAPI {

void PluginList::PlatformInit() {
}

void PluginList::GetPluginDirectories(std::vector<FilePath>* plugin_dirs) {
  // See http://groups.google.com/group/chromium-dev/browse_thread/thread/7a70e5fcbac786a9
  // for discussion.
  // We first consult Chrome-specific dirs, then fall back on the logic
  // Mozilla uses.

  // TODO(evanm): maybe consult our own plugins dir, like
  // ~/.config/chromium/Plugins?

  // The Chrome binary dir + "plugins/".
  FilePath dir;
  PathService::Get(base::DIR_EXE, &dir);
  plugin_dirs->push_back(dir.Append("plugins"));

  // Mozilla code to reference:
  // http://mxr.mozilla.org/firefox/ident?i=NS_APP_PLUGINS_DIR_LIST
  // and tens of accompanying files (mxr is very helpful).
  // This code carefully matches their behavior for compat reasons.

  // 1) MOZ_PLUGIN_PATH env variable.
  const char* moz_plugin_path = getenv("MOZ_PLUGIN_PATH");
  if (moz_plugin_path) {
    std::vector<std::string> paths;
    SplitString(moz_plugin_path, ':', &paths);
    for (size_t i = 0; i < paths.size(); ++i)
      plugin_dirs->push_back(FilePath(paths[i]));
  }

  // 2) NS_USER_PLUGINS_DIR: ~/.mozilla/plugins.
  // This is a de-facto standard, so even though we're not Mozilla, let's
  // look in there too.
  const char* home = getenv("HOME");
  if (home)
    plugin_dirs->push_back(FilePath(home).Append(".mozilla/plugins"));

  // 3) NS_SYSTEM_PLUGINS_DIR:
  // This varies across different versions of Firefox, so check 'em all.
  plugin_dirs->push_back(FilePath("/usr/lib/mozilla/plugins"));
  plugin_dirs->push_back(FilePath("/usr/lib/firefox/plugins"));
  plugin_dirs->push_back(FilePath("/usr/lib/xulrunner-addons/plugins"));
}

void PluginList::LoadPluginsFromDir(const FilePath& path,
                                    std::vector<WebPluginInfo>* plugins) {
  // See ScanPluginsDirectory near
  // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginHostImpl.cpp#5052

  // Construct and stat a list of all filenames under consideration, for
  // later sorting by mtime.
  FileTimeList files;
  file_util::FileEnumerator enumerator(path,
                                       false, // not recursive
                                       file_util::FileEnumerator::FILES);
  for (FilePath path = enumerator.Next(); !path.value().empty();
       path = enumerator.Next()) {
    // Skip over Mozilla .xpt files.
    if (path.MatchesExtension(FILE_PATH_LITERAL(".xpt")))
      continue;

    // Java doesn't like being loaded through a symlink, since it uses
    // its path to find dependent data files.
    // file_util::AbsolutePath calls through to realpath(), which resolves
    // symlinks.
    FilePath orig_path = path;
    file_util::AbsolutePath(&path);
    if (DebugPluginLoading())
      LOG(ERROR) << "Resolved " << orig_path.value() << " -> " << path.value();

    // Get mtime.
    file_util::FileInfo info;
    if (!file_util::GetFileInfo(path, &info))
      continue;

    // Skip duplicates of the same file in our list.
    bool skip = false;
    for (size_t i = 0; i < plugins->size(); ++i) {
      if (plugins->at(i).path == path) {
        skip = true;
        break;
      }
    }
    if (skip) {
      if (DebugPluginLoading())
        LOG(ERROR) << "Skipping duplicate instance of " << path.value();
      continue;
    }

    files.push_back(std::make_pair(path, info.last_modified));
  }

  // Sort the file list by time (and filename).
  std::sort(files.begin(), files.end(), CompareTime);

  // Load the files in order.
  for (FileTimeList::const_iterator i = files.begin(); i != files.end(); ++i) {
    LoadPlugin(i->first, plugins);
  }
}

bool PluginList::ShouldLoadPlugin(const WebPluginInfo& info,
                                  std::vector<WebPluginInfo>* plugins) {
  if (DebugPluginLoading()) {
    LOG(ERROR) << "Considering " << info.path.value()
              << " (" << info.name << ")";
  }
  if (IsUndesirablePlugin(info)) {
    if (DebugPluginLoading())
      LOG(ERROR) << info.path.value() << " is undesirable.";

    // See if we have a better version of this plugin.
    for (size_t i = 0; i < plugins->size(); ++i) {
      if (plugins->at(i).name == info.name &&
          !IsUndesirablePlugin(plugins->at(i))) {
        // Skip the current undesirable one so we can use the better one
        // we just found.
        if (DebugPluginLoading()) {
          LOG(ERROR) << "Skipping " << info.path.value() << ", preferring "
                    << plugins->at(i).path.value();
        }
        return false;
      }
    }
  }

  // TODO(evanm): prefer the newest version of flash, etc. here?

  if (DebugPluginLoading())
    LOG(ERROR) << "Using " << info.path.value();

  return true;
}

} // namespace NPAPI
<commit_msg>linux: defend against "netscape" within plugin path<commit_after>// Copyright (c) 2009 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 "webkit/glue/plugins/plugin_list.h"

#include "base/command_line.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/string_util.h"

namespace {

// Return true if we're in debug-plugin-loading mode.
bool DebugPluginLoading() {
  static const wchar_t kDebugPluginLoading[] = L"debug-plugin-loading";
  return CommandLine::ForCurrentProcess()->HasSwitch(kDebugPluginLoading);
}

// We build up a list of files and mtimes so we can sort them.
typedef std::pair<FilePath, base::Time> FileAndTime;
typedef std::vector<FileAndTime> FileTimeList;

// Comparator used to sort by descending mtime then ascending filename.
bool CompareTime(const FileAndTime& a, const FileAndTime& b) {
  if (a.second == b.second) {
    // Fall back on filename sorting, just to make the predicate valid.
    return a.first < b.first;
  }

  // Sort by mtime, descending.
  return a.second > b.second;
}

// Some plugins are shells around other plugins; we prefer to use the
// real plugin directly, if it's available.  This function returns
// true if we should prefer other plugins over this one.  We'll still
// use a "undesirable" plugin if no other option is available.
bool IsUndesirablePlugin(const WebPluginInfo& info) {
  std::string filename = info.path.BaseName().value();
  return (filename.find("npcxoffice") != std::string::npos || // Crossover
          filename.find("npwrapper") != std::string::npos);   // nspluginwrapper
}

}  // anonymous namespace

namespace NPAPI {

void PluginList::PlatformInit() {
}

void PluginList::GetPluginDirectories(std::vector<FilePath>* plugin_dirs) {
  // See http://groups.google.com/group/chromium-dev/browse_thread/thread/7a70e5fcbac786a9
  // for discussion.
  // We first consult Chrome-specific dirs, then fall back on the logic
  // Mozilla uses.

  // TODO(evanm): maybe consult our own plugins dir, like
  // ~/.config/chromium/Plugins?

  // The Chrome binary dir + "plugins/".
  FilePath dir;
  PathService::Get(base::DIR_EXE, &dir);
  plugin_dirs->push_back(dir.Append("plugins"));

  // Mozilla code to reference:
  // http://mxr.mozilla.org/firefox/ident?i=NS_APP_PLUGINS_DIR_LIST
  // and tens of accompanying files (mxr is very helpful).
  // This code carefully matches their behavior for compat reasons.

  // 1) MOZ_PLUGIN_PATH env variable.
  const char* moz_plugin_path = getenv("MOZ_PLUGIN_PATH");
  if (moz_plugin_path) {
    std::vector<std::string> paths;
    SplitString(moz_plugin_path, ':', &paths);
    for (size_t i = 0; i < paths.size(); ++i)
      plugin_dirs->push_back(FilePath(paths[i]));
  }

  // 2) NS_USER_PLUGINS_DIR: ~/.mozilla/plugins.
  // This is a de-facto standard, so even though we're not Mozilla, let's
  // look in there too.
  const char* home = getenv("HOME");
  if (home)
    plugin_dirs->push_back(FilePath(home).Append(".mozilla/plugins"));

  // 3) NS_SYSTEM_PLUGINS_DIR:
  // This varies across different versions of Firefox, so check 'em all.
  plugin_dirs->push_back(FilePath("/usr/lib/mozilla/plugins"));
  plugin_dirs->push_back(FilePath("/usr/lib/firefox/plugins"));
  plugin_dirs->push_back(FilePath("/usr/lib/xulrunner-addons/plugins"));
}

void PluginList::LoadPluginsFromDir(const FilePath& path,
                                    std::vector<WebPluginInfo>* plugins) {
  // See ScanPluginsDirectory near
  // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginHostImpl.cpp#5052

  // Construct and stat a list of all filenames under consideration, for
  // later sorting by mtime.
  FileTimeList files;
  file_util::FileEnumerator enumerator(path,
                                       false, // not recursive
                                       file_util::FileEnumerator::FILES);
  for (FilePath path = enumerator.Next(); !path.value().empty();
       path = enumerator.Next()) {
    // Skip over Mozilla .xpt files.
    if (path.MatchesExtension(FILE_PATH_LITERAL(".xpt")))
      continue;

    // Java doesn't like being loaded through a symlink, since it uses
    // its path to find dependent data files.
    // file_util::AbsolutePath calls through to realpath(), which resolves
    // symlinks.
    FilePath orig_path = path;
    file_util::AbsolutePath(&path);
    if (DebugPluginLoading())
      LOG(ERROR) << "Resolved " << orig_path.value() << " -> " << path.value();

    // Flash stops working if the containing directory involves 'netscape'.
    // No joke.  So use the other path if it's better.
    static const char kFlashPlayerFilename[] = "libflashplayer.so";
    static const char kNetscapeInPath[] = "/netscape/";
    if (path.BaseName().value() == kFlashPlayerFilename &&
        path.value().find(kNetscapeInPath) != std::string::npos) {
      if (orig_path.value().find(kNetscapeInPath) == std::string::npos) {
        // Go back to the old path.
        path = orig_path;
      } else {
        LOG(ERROR) << "Flash misbehaves when used from a directory containing "
                   << kNetscapeInPath << ", so skipping " << orig_path.value();
        continue;
      }
    }

    // Get mtime.
    file_util::FileInfo info;
    if (!file_util::GetFileInfo(path, &info))
      continue;

    // Skip duplicates of the same file in our list.
    bool skip = false;
    for (size_t i = 0; i < plugins->size(); ++i) {
      if (plugins->at(i).path == path) {
        skip = true;
        break;
      }
    }
    if (skip) {
      if (DebugPluginLoading())
        LOG(ERROR) << "Skipping duplicate instance of " << path.value();
      continue;
    }

    files.push_back(std::make_pair(path, info.last_modified));
  }

  // Sort the file list by time (and filename).
  std::sort(files.begin(), files.end(), CompareTime);

  // Load the files in order.
  for (FileTimeList::const_iterator i = files.begin(); i != files.end(); ++i) {
    LoadPlugin(i->first, plugins);
  }
}

bool PluginList::ShouldLoadPlugin(const WebPluginInfo& info,
                                  std::vector<WebPluginInfo>* plugins) {
  if (DebugPluginLoading()) {
    LOG(ERROR) << "Considering " << info.path.value()
              << " (" << info.name << ")";
  }
  if (IsUndesirablePlugin(info)) {
    if (DebugPluginLoading())
      LOG(ERROR) << info.path.value() << " is undesirable.";

    // See if we have a better version of this plugin.
    for (size_t i = 0; i < plugins->size(); ++i) {
      if (plugins->at(i).name == info.name &&
          !IsUndesirablePlugin(plugins->at(i))) {
        // Skip the current undesirable one so we can use the better one
        // we just found.
        if (DebugPluginLoading()) {
          LOG(ERROR) << "Skipping " << info.path.value() << ", preferring "
                    << plugins->at(i).path.value();
        }
        return false;
      }
    }
  }

  // TODO(evanm): prefer the newest version of flash, etc. here?

  if (DebugPluginLoading())
    LOG(ERROR) << "Using " << info.path.value();

  return true;
}

} // namespace NPAPI
<|endoftext|>


<commit_before>// Copyright (c) 2009 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/chromeos/main_menu.h"

#include <string>
#include <vector>

#include "app/gfx/insets.h"
#include "app/resource_bundle.h"
#include "base/command_line.h"
#include "base/message_loop.h"
#include "base/string_util.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/renderer_host/render_view_host_factory.h"
#include "chrome/browser/renderer_host/render_widget_host_view.h"
#include "chrome/browser/renderer_host/render_widget_host_view_gtk.h"
#include "chrome/browser/renderer_host/site_instance.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/tab_contents/render_view_host_delegate_helper.h"
#include "grit/app_resources.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "views/background.h"
#include "views/painter.h"
#include "views/screen.h"
#include "views/widget/root_view.h"
#include "views/widget/widget_gtk.h"

namespace chromeos {

// Initial size of the renderer. This is contained within a window whose size
// is set to the size of the image IDR_MAIN_MENU_BUTTON_DROP_DOWN.
static const int kRendererX = 0;
static const int kRendererY = 25;
static const int kRendererWidth = 250;
static const int kRendererHeight = 400;

// Insets defining the regions that are stretched and titled to create the
// background of the popup. These constants are fed into
// Painter::CreateImagePainter as the insets, see it for details.
static const int kBackgroundImageTop = 27;
static const int kBackgroundImageLeft = 85;
static const int kBackgroundImageBottom = 10;
static const int kBackgroundImageRight = 8;

// Command line switch for specifying url of the page.
static const wchar_t kURLSwitch[] = L"main-menu-url";

// Command line switch for specifying the size of the main menu. The default is
// full screen.
static const wchar_t kMenuSizeSwitch[] = L"main-menu-size";

// URL of the page to load. This is ignored if kURLSwitch is specified.
static const char kMenuURL[] = "http://goto.ext.google.com/crux-home";

// Returns the size of the popup. By default the popup is sized slightly
// larger than full screen, but can be overriden by the command line switch
// kMenuSizeSwitch.
static gfx::Size GetPopupSize() {
  std::wstring cl_size =
      CommandLine::ForCurrentProcess()->GetSwitchValue(kMenuSizeSwitch);
  if (!cl_size.empty()) {
    std::vector<std::string> chunks;
    SplitString(WideToUTF8(cl_size), 'x', &chunks);
    if (chunks.size() == 2)
      return gfx::Size(StringToInt(chunks[0]), StringToInt(chunks[1]));
  }

  gfx::Size size =
      views::Screen::GetMonitorAreaNearestPoint(gfx::Point(0, 0)).size();
  // When full screen we don't want to see the drop shadow. Adjust the width
  // so the drop shadow ends up off screen.
  size.Enlarge(kBackgroundImageRight, kBackgroundImageBottom);
  return size;
}

// Returns the size for the renderer widget host view given the specified
// size of the popup.
static gfx::Size CalculateRWHVSize(const gfx::Size& popup_size) {
  return gfx::Size(popup_size.width() - kRendererX - kBackgroundImageRight,
                   popup_size.height() - kRendererY - kBackgroundImageBottom);
}

// Returns the URL of the menu.
static GURL GetMenuURL() {
  std::wstring url_string =
      CommandLine::ForCurrentProcess()->GetSwitchValue(kURLSwitch);
  if (!url_string.empty())
    return GURL(WideToUTF8(url_string));
  return GURL(kMenuURL);
}

// static
void MainMenu::Show(Browser* browser) {
  MainMenu::Get()->ShowImpl(browser);
}

// static
void MainMenu::ScheduleCreation() {
  MessageLoop::current()->PostDelayedTask(FROM_HERE, new LoadTask(), 5000);
}

MainMenu::~MainMenu() {
  // NOTE: we leak the menu_rvh_ and popup_ as by the time we get here the
  // message loop and notification services have been shutdown.
  // TODO(sky): fix this.
  // menu_rvh_->Shutdown();
  // popup_->CloseNow();
}

MainMenu::MainMenu()
    : browser_(NULL),
      popup_(NULL),
      site_instance_(NULL),
      menu_rvh_(NULL),
      rwhv_(NULL),
      ALLOW_THIS_IN_INITIALIZER_LIST(tab_contents_delegate_(this)),
      ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
      has_shown_(false) {
  SkBitmap* drop_down_image = ResourceBundle::GetSharedInstance().
      GetBitmapNamed(IDR_MAIN_MENU_BUTTON_DROP_DOWN);

  views::WidgetGtk* menu_popup =
      new views::WidgetGtk(views::WidgetGtk::TYPE_POPUP);
  popup_ = menu_popup;
  // The background image has transparency, so we make the window transparent.
  menu_popup->MakeTransparent();
  gfx::Size popup_size = GetPopupSize();
  menu_popup->Init(NULL, gfx::Rect(0, 0, popup_size.width(),
                                   popup_size.height()));

  views::Painter* painter = views::Painter::CreateImagePainter(
      *drop_down_image,
      gfx::Insets(kBackgroundImageTop, kBackgroundImageLeft,
                  kBackgroundImageBottom, kBackgroundImageRight),
      false);
  menu_popup->GetRootView()->set_background(
      views::Background::CreateBackgroundPainter(true, painter));

  GURL menu_url(GetMenuURL());
  DCHECK(BrowserList::begin() != BrowserList::end());
  // TODO(sky): this shouldn't pick a random profile to use.
  site_instance_ = SiteInstance::CreateSiteInstanceForURL(
      (*BrowserList::begin())->profile(), menu_url);
  menu_rvh_ = new RenderViewHost(site_instance_, this, MSG_ROUTING_NONE);

  rwhv_ = new RenderWidgetHostViewGtk(menu_rvh_);
  rwhv_->InitAsChild();
  menu_rvh_->CreateRenderView(browser_->profile()->GetRequestContext());
  menu_popup->AddChild(rwhv_->GetNativeView());
  gfx::Size rwhv_size = CalculateRWHVSize(popup_size);
  menu_popup->PositionChild(rwhv_->GetNativeView(), kRendererX, kRendererY,
                            rwhv_size.width(), rwhv_size.height());
  rwhv_->SetSize(rwhv_size);
  menu_rvh_->NavigateToURL(menu_url);
}

// static
MainMenu* MainMenu::Get() {
  return Singleton<MainMenu>::get();
}

void MainMenu::ShowImpl(Browser* browser) {
  Cleanup();

  browser_ = browser;

  popup_->Show();

  GtkWidget* rwhv_widget = rwhv_->GetNativeView();

  if (!has_shown_) {
    has_shown_ = true;
    gtk_widget_realize(rwhv_widget);
    g_signal_connect(rwhv_widget, "button-press-event",
                     G_CALLBACK(CallButtonPressEvent), this);
  }

  // Do a mouse grab on the renderer widget host view's widget so that we can
  // close the popup if the user clicks anywhere else. And do a keyboard
  // grab so that we get all key events.
  gdk_pointer_grab(rwhv_widget->window, FALSE,
                   static_cast<GdkEventMask>(
                         GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
                         GDK_POINTER_MOTION_MASK),
                   NULL, NULL, GDK_CURRENT_TIME);
  gdk_keyboard_grab(rwhv_widget->window, FALSE, GDK_CURRENT_TIME);
}

void MainMenu::Hide() {
  gdk_keyboard_ungrab(GDK_CURRENT_TIME);
  gdk_pointer_ungrab(GDK_CURRENT_TIME);

  popup_->Hide();

  // The stack may have pending_contents_ on it. Delay deleting the
  // pending_contents_ as TabContents doesn't deal well with being deleted
  // while on the stack.
  MessageLoop::current()->PostTask(FROM_HERE,
      method_factory_.NewRunnableMethod(&MainMenu::Cleanup));
}

void MainMenu::Cleanup() {
  pending_contents_.reset(NULL);
  method_factory_.RevokeAll();
}

// static
gboolean MainMenu::CallButtonPressEvent(GtkWidget* widget,
                                        GdkEventButton* event,
                                        MainMenu* menu) {
  return menu->OnButtonPressEvent(widget, event);
}

gboolean MainMenu::OnButtonPressEvent(GtkWidget* widget,
                                      GdkEventButton* event) {
  if (event->x < 0 || event->y < 0 ||
      event->x >= widget->allocation.width ||
      event->y >= widget->allocation.height) {
    // The user clicked outside the bounds of the menu, delete the main which
    // results in closing it.
    Hide();
  }
  return FALSE;
}

void MainMenu::RequestMove(const gfx::Rect& new_bounds) {
  // Invoking PositionChild results in a gtk signal that triggers attempting to
  // to resize the window. We need to set the size request so that it resizes
  // correctly when this happens.
  gtk_widget_set_size_request(popup_->GetNativeView(),
                              new_bounds.width(), new_bounds.height());
  gfx::Size rwhv_size = CalculateRWHVSize(new_bounds.size());
  popup_->PositionChild(rwhv_->GetNativeView(), kRendererX, kRendererY,
                        rwhv_size.width(), rwhv_size.height());
  popup_->SetBounds(new_bounds);
  rwhv_->SetSize(rwhv_size);
}

void MainMenu::CreateNewWindow(int route_id) {
  if (pending_contents_.get()) {
    NOTREACHED();
    return;
  }

  helper_.CreateNewWindow(route_id, browser_->profile(), site_instance_,
                          DOMUIFactory::GetDOMUIType(GURL(GetMenuURL())),
                          NULL);
  pending_contents_.reset(helper_.GetCreatedWindow(route_id));
  pending_contents_->set_delegate(&tab_contents_delegate_);
}

void MainMenu::ShowCreatedWindow(int route_id,
                                 WindowOpenDisposition disposition,
                                 const gfx::Rect& initial_pos,
                                 bool user_gesture,
                                 const GURL& creator_url) {
  if (disposition == NEW_POPUP) {
    pending_contents_->set_delegate(NULL);
    browser_->GetSelectedTabContents()->AddNewContents(
        pending_contents_.release(), disposition, initial_pos, user_gesture,
        creator_url);
    Hide();
  }
}

void MainMenu::TabContentsDelegateImpl::OpenURLFromTab(
    TabContents* source,
    const GURL& url,
    const GURL& referrer,
    WindowOpenDisposition disposition,
    PageTransition::Type transition) {
  menu_->browser_->OpenURL(url, referrer, NEW_FOREGROUND_TAB,
                           PageTransition::LINK);
  menu_->Hide();
}

// LoadTask -------------------------------------------------------------------

void MainMenu::LoadTask::Run() {
  MainMenu::Get();
}

}  // namespace chromeos
<commit_msg>Fix crashing bug due to browser being NULL. BUG=None TEST=None Review URL: http://codereview.chromium.org/400015<commit_after>// Copyright (c) 2009 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/chromeos/main_menu.h"

#include <string>
#include <vector>

#include "app/gfx/insets.h"
#include "app/resource_bundle.h"
#include "base/command_line.h"
#include "base/message_loop.h"
#include "base/string_util.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/renderer_host/render_view_host_factory.h"
#include "chrome/browser/renderer_host/render_widget_host_view.h"
#include "chrome/browser/renderer_host/render_widget_host_view_gtk.h"
#include "chrome/browser/renderer_host/site_instance.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/tab_contents/render_view_host_delegate_helper.h"
#include "grit/app_resources.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "views/background.h"
#include "views/painter.h"
#include "views/screen.h"
#include "views/widget/root_view.h"
#include "views/widget/widget_gtk.h"

namespace chromeos {

// Initial size of the renderer. This is contained within a window whose size
// is set to the size of the image IDR_MAIN_MENU_BUTTON_DROP_DOWN.
static const int kRendererX = 0;
static const int kRendererY = 25;
static const int kRendererWidth = 250;
static const int kRendererHeight = 400;

// Insets defining the regions that are stretched and titled to create the
// background of the popup. These constants are fed into
// Painter::CreateImagePainter as the insets, see it for details.
static const int kBackgroundImageTop = 27;
static const int kBackgroundImageLeft = 85;
static const int kBackgroundImageBottom = 10;
static const int kBackgroundImageRight = 8;

// Command line switch for specifying url of the page.
static const wchar_t kURLSwitch[] = L"main-menu-url";

// Command line switch for specifying the size of the main menu. The default is
// full screen.
static const wchar_t kMenuSizeSwitch[] = L"main-menu-size";

// URL of the page to load. This is ignored if kURLSwitch is specified.
static const char kMenuURL[] = "http://goto.ext.google.com/crux-home";

// Returns the size of the popup. By default the popup is sized slightly
// larger than full screen, but can be overriden by the command line switch
// kMenuSizeSwitch.
static gfx::Size GetPopupSize() {
  std::wstring cl_size =
      CommandLine::ForCurrentProcess()->GetSwitchValue(kMenuSizeSwitch);
  if (!cl_size.empty()) {
    std::vector<std::string> chunks;
    SplitString(WideToUTF8(cl_size), 'x', &chunks);
    if (chunks.size() == 2)
      return gfx::Size(StringToInt(chunks[0]), StringToInt(chunks[1]));
  }

  gfx::Size size =
      views::Screen::GetMonitorAreaNearestPoint(gfx::Point(0, 0)).size();
  // When full screen we don't want to see the drop shadow. Adjust the width
  // so the drop shadow ends up off screen.
  size.Enlarge(kBackgroundImageRight, kBackgroundImageBottom);
  return size;
}

// Returns the size for the renderer widget host view given the specified
// size of the popup.
static gfx::Size CalculateRWHVSize(const gfx::Size& popup_size) {
  return gfx::Size(popup_size.width() - kRendererX - kBackgroundImageRight,
                   popup_size.height() - kRendererY - kBackgroundImageBottom);
}

// Returns the URL of the menu.
static GURL GetMenuURL() {
  std::wstring url_string =
      CommandLine::ForCurrentProcess()->GetSwitchValue(kURLSwitch);
  if (!url_string.empty())
    return GURL(WideToUTF8(url_string));
  return GURL(kMenuURL);
}

// static
void MainMenu::Show(Browser* browser) {
  MainMenu::Get()->ShowImpl(browser);
}

// static
void MainMenu::ScheduleCreation() {
  MessageLoop::current()->PostDelayedTask(FROM_HERE, new LoadTask(), 5000);
}

MainMenu::~MainMenu() {
  // NOTE: we leak the menu_rvh_ and popup_ as by the time we get here the
  // message loop and notification services have been shutdown.
  // TODO(sky): fix this.
  // menu_rvh_->Shutdown();
  // popup_->CloseNow();
}

MainMenu::MainMenu()
    : browser_(NULL),
      popup_(NULL),
      site_instance_(NULL),
      menu_rvh_(NULL),
      rwhv_(NULL),
      ALLOW_THIS_IN_INITIALIZER_LIST(tab_contents_delegate_(this)),
      ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
      has_shown_(false) {
  SkBitmap* drop_down_image = ResourceBundle::GetSharedInstance().
      GetBitmapNamed(IDR_MAIN_MENU_BUTTON_DROP_DOWN);

  views::WidgetGtk* menu_popup =
      new views::WidgetGtk(views::WidgetGtk::TYPE_POPUP);
  popup_ = menu_popup;
  // The background image has transparency, so we make the window transparent.
  menu_popup->MakeTransparent();
  gfx::Size popup_size = GetPopupSize();
  menu_popup->Init(NULL, gfx::Rect(0, 0, popup_size.width(),
                                   popup_size.height()));

  views::Painter* painter = views::Painter::CreateImagePainter(
      *drop_down_image,
      gfx::Insets(kBackgroundImageTop, kBackgroundImageLeft,
                  kBackgroundImageBottom, kBackgroundImageRight),
      false);
  menu_popup->GetRootView()->set_background(
      views::Background::CreateBackgroundPainter(true, painter));

  GURL menu_url(GetMenuURL());
  DCHECK(BrowserList::begin() != BrowserList::end());
  // TODO(sky): this shouldn't pick a random profile to use.
  Profile* profile = (*BrowserList::begin())->profile();
  site_instance_ = SiteInstance::CreateSiteInstanceForURL(profile, menu_url);
  menu_rvh_ = new RenderViewHost(site_instance_, this, MSG_ROUTING_NONE);

  rwhv_ = new RenderWidgetHostViewGtk(menu_rvh_);
  rwhv_->InitAsChild();
  menu_rvh_->CreateRenderView(profile->GetRequestContext());
  menu_popup->AddChild(rwhv_->GetNativeView());
  gfx::Size rwhv_size = CalculateRWHVSize(popup_size);
  menu_popup->PositionChild(rwhv_->GetNativeView(), kRendererX, kRendererY,
                            rwhv_size.width(), rwhv_size.height());
  rwhv_->SetSize(rwhv_size);
  menu_rvh_->NavigateToURL(menu_url);
}

// static
MainMenu* MainMenu::Get() {
  return Singleton<MainMenu>::get();
}

void MainMenu::ShowImpl(Browser* browser) {
  Cleanup();

  browser_ = browser;

  popup_->Show();

  GtkWidget* rwhv_widget = rwhv_->GetNativeView();

  if (!has_shown_) {
    has_shown_ = true;
    gtk_widget_realize(rwhv_widget);
    g_signal_connect(rwhv_widget, "button-press-event",
                     G_CALLBACK(CallButtonPressEvent), this);
  }

  // Do a mouse grab on the renderer widget host view's widget so that we can
  // close the popup if the user clicks anywhere else. And do a keyboard
  // grab so that we get all key events.
  gdk_pointer_grab(rwhv_widget->window, FALSE,
                   static_cast<GdkEventMask>(
                         GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
                         GDK_POINTER_MOTION_MASK),
                   NULL, NULL, GDK_CURRENT_TIME);
  gdk_keyboard_grab(rwhv_widget->window, FALSE, GDK_CURRENT_TIME);
}

void MainMenu::Hide() {
  gdk_keyboard_ungrab(GDK_CURRENT_TIME);
  gdk_pointer_ungrab(GDK_CURRENT_TIME);

  popup_->Hide();

  // The stack may have pending_contents_ on it. Delay deleting the
  // pending_contents_ as TabContents doesn't deal well with being deleted
  // while on the stack.
  MessageLoop::current()->PostTask(FROM_HERE,
      method_factory_.NewRunnableMethod(&MainMenu::Cleanup));
}

void MainMenu::Cleanup() {
  pending_contents_.reset(NULL);
  method_factory_.RevokeAll();
}

// static
gboolean MainMenu::CallButtonPressEvent(GtkWidget* widget,
                                        GdkEventButton* event,
                                        MainMenu* menu) {
  return menu->OnButtonPressEvent(widget, event);
}

gboolean MainMenu::OnButtonPressEvent(GtkWidget* widget,
                                      GdkEventButton* event) {
  if (event->x < 0 || event->y < 0 ||
      event->x >= widget->allocation.width ||
      event->y >= widget->allocation.height) {
    // The user clicked outside the bounds of the menu, delete the main which
    // results in closing it.
    Hide();
  }
  return FALSE;
}

void MainMenu::RequestMove(const gfx::Rect& new_bounds) {
  // Invoking PositionChild results in a gtk signal that triggers attempting to
  // to resize the window. We need to set the size request so that it resizes
  // correctly when this happens.
  gtk_widget_set_size_request(popup_->GetNativeView(),
                              new_bounds.width(), new_bounds.height());
  gfx::Size rwhv_size = CalculateRWHVSize(new_bounds.size());
  popup_->PositionChild(rwhv_->GetNativeView(), kRendererX, kRendererY,
                        rwhv_size.width(), rwhv_size.height());
  popup_->SetBounds(new_bounds);
  rwhv_->SetSize(rwhv_size);
}

void MainMenu::CreateNewWindow(int route_id) {
  if (pending_contents_.get()) {
    NOTREACHED();
    return;
  }

  helper_.CreateNewWindow(route_id, browser_->profile(), site_instance_,
                          DOMUIFactory::GetDOMUIType(GURL(GetMenuURL())),
                          NULL);
  pending_contents_.reset(helper_.GetCreatedWindow(route_id));
  pending_contents_->set_delegate(&tab_contents_delegate_);
}

void MainMenu::ShowCreatedWindow(int route_id,
                                 WindowOpenDisposition disposition,
                                 const gfx::Rect& initial_pos,
                                 bool user_gesture,
                                 const GURL& creator_url) {
  if (disposition == NEW_POPUP) {
    pending_contents_->set_delegate(NULL);
    browser_->GetSelectedTabContents()->AddNewContents(
        pending_contents_.release(), disposition, initial_pos, user_gesture,
        creator_url);
    Hide();
  }
}

void MainMenu::TabContentsDelegateImpl::OpenURLFromTab(
    TabContents* source,
    const GURL& url,
    const GURL& referrer,
    WindowOpenDisposition disposition,
    PageTransition::Type transition) {
  menu_->browser_->OpenURL(url, referrer, NEW_FOREGROUND_TAB,
                           PageTransition::LINK);
  menu_->Hide();
}

// LoadTask -------------------------------------------------------------------

void MainMenu::LoadTask::Run() {
  MainMenu::Get();
}

}  // namespace chromeos
<|endoftext|>