1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/api/alarms/alarms_api.h"
6
7#include "base/strings/string_number_conversions.h"
8#include "base/time/clock.h"
9#include "base/time/default_clock.h"
10#include "base/values.h"
11#include "chrome/browser/extensions/api/alarms/alarm_manager.h"
12#include "chrome/common/extensions/api/alarms.h"
13#include "extensions/common/error_utils.h"
14
15namespace alarms = extensions::api::alarms;
16
17namespace extensions {
18
19namespace {
20
21const char kDefaultAlarmName[] = "";
22const char kBothRelativeAndAbsoluteTime[] =
23    "Cannot set both when and delayInMinutes.";
24const char kNoScheduledTime[] =
25    "Must set at least one of when, delayInMinutes, or periodInMinutes.";
26const int kReleaseDelayMinimum = 1;
27const int kDevDelayMinimum = 0;
28
29bool ValidateAlarmCreateInfo(const std::string& alarm_name,
30                             const alarms::AlarmCreateInfo& create_info,
31                             const Extension* extension,
32                             std::string* error,
33                             std::vector<std::string>* warnings) {
34  if (create_info.delay_in_minutes.get() &&
35      create_info.when.get()) {
36    *error = kBothRelativeAndAbsoluteTime;
37    return false;
38  }
39  if (create_info.delay_in_minutes == NULL &&
40      create_info.when == NULL &&
41      create_info.period_in_minutes == NULL) {
42    *error = kNoScheduledTime;
43    return false;
44  }
45
46  // Users can always use an absolute timeout to request an arbitrarily-short or
47  // negative delay.  We won't honor the short timeout, but we can't check it
48  // and warn the user because it would introduce race conditions (say they
49  // compute a long-enough timeout, but then the call into the alarms interface
50  // gets delayed past the boundary).  However, it's still worth warning about
51  // relative delays that are shorter than we'll honor.
52  if (create_info.delay_in_minutes.get()) {
53    if (*create_info.delay_in_minutes < kReleaseDelayMinimum) {
54      COMPILE_ASSERT(kReleaseDelayMinimum == 1, update_warning_message_below);
55      if (Manifest::IsUnpackedLocation(extension->location()))
56        warnings->push_back(ErrorUtils::FormatErrorMessage(
57            "Alarm delay is less than minimum of 1 minutes."
58            " In released .crx, alarm \"*\" will fire in approximately"
59            " 1 minutes.",
60            alarm_name));
61      else
62        warnings->push_back(ErrorUtils::FormatErrorMessage(
63            "Alarm delay is less than minimum of 1 minutes."
64            " Alarm \"*\" will fire in approximately 1 minutes.",
65            alarm_name));
66    }
67  }
68  if (create_info.period_in_minutes.get()) {
69    if (*create_info.period_in_minutes < kReleaseDelayMinimum) {
70      COMPILE_ASSERT(kReleaseDelayMinimum == 1, update_warning_message_below);
71      if (Manifest::IsUnpackedLocation(extension->location()))
72        warnings->push_back(ErrorUtils::FormatErrorMessage(
73            "Alarm period is less than minimum of 1 minutes."
74            " In released .crx, alarm \"*\" will fire approximately"
75            " every 1 minutes.",
76            alarm_name));
77      else
78        warnings->push_back(ErrorUtils::FormatErrorMessage(
79            "Alarm period is less than minimum of 1 minutes."
80            " Alarm \"*\" will fire approximately every 1 minutes.",
81            alarm_name));
82    }
83  }
84
85  return true;
86}
87
88}  // namespace
89
90AlarmsCreateFunction::AlarmsCreateFunction()
91    : clock_(new base::DefaultClock()), owns_clock_(true) {}
92
93AlarmsCreateFunction::AlarmsCreateFunction(base::Clock* clock)
94    : clock_(clock), owns_clock_(false) {}
95
96AlarmsCreateFunction::~AlarmsCreateFunction() {
97  if (owns_clock_)
98    delete clock_;
99}
100
101bool AlarmsCreateFunction::RunAsync() {
102  scoped_ptr<alarms::Create::Params> params(
103      alarms::Create::Params::Create(*args_));
104  EXTENSION_FUNCTION_VALIDATE(params.get());
105  const std::string& alarm_name =
106      params->name.get() ? *params->name : kDefaultAlarmName;
107  std::vector<std::string> warnings;
108  if (!ValidateAlarmCreateInfo(
109          alarm_name, params->alarm_info, extension(), &error_, &warnings)) {
110    return false;
111  }
112  for (std::vector<std::string>::const_iterator it = warnings.begin();
113       it != warnings.end(); ++it)
114    WriteToConsole(content::CONSOLE_MESSAGE_LEVEL_WARNING, *it);
115
116  Alarm alarm(alarm_name,
117              params->alarm_info,
118              base::TimeDelta::FromMinutes(
119                  Manifest::IsUnpackedLocation(extension()->location())
120                      ? kDevDelayMinimum
121                      : kReleaseDelayMinimum),
122              clock_->Now());
123  AlarmManager::Get(browser_context())->AddAlarm(
124      extension_id(), alarm, base::Bind(&AlarmsCreateFunction::Callback, this));
125
126  return true;
127}
128
129void AlarmsCreateFunction::Callback() {
130  SendResponse(true);
131}
132
133bool AlarmsGetFunction::RunAsync() {
134  scoped_ptr<alarms::Get::Params> params(alarms::Get::Params::Create(*args_));
135  EXTENSION_FUNCTION_VALIDATE(params.get());
136
137  std::string name = params->name.get() ? *params->name : kDefaultAlarmName;
138  AlarmManager::Get(browser_context())
139      ->GetAlarm(extension_id(),
140                 name,
141                 base::Bind(&AlarmsGetFunction::Callback, this, name));
142
143  return true;
144}
145
146void AlarmsGetFunction::Callback(
147    const std::string& name, extensions::Alarm* alarm) {
148  if (alarm) {
149    results_ = alarms::Get::Results::Create(*alarm->js_alarm);
150  }
151  SendResponse(true);
152}
153
154bool AlarmsGetAllFunction::RunAsync() {
155  AlarmManager::Get(browser_context())->GetAllAlarms(
156      extension_id(), base::Bind(&AlarmsGetAllFunction::Callback, this));
157  return true;
158}
159
160void AlarmsGetAllFunction::Callback(const AlarmList* alarms) {
161  if (alarms) {
162    std::vector<linked_ptr<extensions::api::alarms::Alarm> > create_arg;
163    create_arg.reserve(alarms->size());
164    for (size_t i = 0, size = alarms->size(); i < size; ++i) {
165      create_arg.push_back((*alarms)[i].js_alarm);
166    }
167    results_ = alarms::GetAll::Results::Create(create_arg);
168  } else {
169    SetResult(new base::ListValue());
170  }
171  SendResponse(true);
172}
173
174bool AlarmsClearFunction::RunAsync() {
175  scoped_ptr<alarms::Clear::Params> params(
176      alarms::Clear::Params::Create(*args_));
177  EXTENSION_FUNCTION_VALIDATE(params.get());
178
179  std::string name = params->name.get() ? *params->name : kDefaultAlarmName;
180  AlarmManager::Get(browser_context())
181      ->RemoveAlarm(extension_id(),
182                    name,
183                    base::Bind(&AlarmsClearFunction::Callback, this, name));
184
185  return true;
186}
187
188void AlarmsClearFunction::Callback(const std::string& name, bool success) {
189  SetResult(new base::FundamentalValue(success));
190  SendResponse(true);
191}
192
193bool AlarmsClearAllFunction::RunAsync() {
194  AlarmManager::Get(browser_context())->RemoveAllAlarms(
195      extension_id(), base::Bind(&AlarmsClearAllFunction::Callback, this));
196  return true;
197}
198
199void AlarmsClearAllFunction::Callback() {
200  SetResult(new base::FundamentalValue(true));
201  SendResponse(true);
202}
203
204}  // namespace extensions
205