summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions/api/declarative_content/content_action.cc
blob: cf279690d2b94a7b25da00590dddf69d7ab28e0b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// 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/extensions/api/declarative_content/content_action.h"

#include <map>

#include "base/lazy_instance.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/extensions/api/declarative_content/content_constants.h"
#include "chrome/browser/extensions/extension_action.h"
#include "chrome/browser/extensions/extension_action_manager.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/invalidate_type.h"
#include "content/public/browser/web_contents.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/extension.h"

namespace extensions {

namespace keys = declarative_content_constants;

namespace {
// Error messages.
const char kInvalidInstanceTypeError[] =
    "An action has an invalid instanceType: %s";
const char kNoPageAction[] =
    "Can't use declarativeContent.ShowPageAction without a page action";

#define INPUT_FORMAT_VALIDATE(test) do { \
    if (!(test)) { \
      *bad_message = true; \
      return scoped_refptr<ContentAction>(NULL); \
    } \
  } while (0)

//
// The following are concrete actions.
//

// Action that instructs to show an extension's page action.
class ShowPageAction : public ContentAction {
 public:
  ShowPageAction() {}

  static scoped_refptr<ContentAction> Create(const Extension* extension,
                                             const base::DictionaryValue* dict,
                                             std::string* error,
                                             bool* bad_message) {
    // We can't show a page action if the extension doesn't have one.
    if (ActionInfo::GetPageActionInfo(extension) == NULL) {
      *error = kNoPageAction;
      return scoped_refptr<ContentAction>();
    }
    return scoped_refptr<ContentAction>(new ShowPageAction);
  }

  // Implementation of ContentAction:
  virtual Type GetType() const OVERRIDE { return ACTION_SHOW_PAGE_ACTION; }
  virtual void Apply(const std::string& extension_id,
                     const base::Time& extension_install_time,
                     ApplyInfo* apply_info) const OVERRIDE {
    GetPageAction(apply_info->profile, extension_id)->DeclarativeShow(
        ExtensionTabUtil::GetTabId(apply_info->tab));
    apply_info->tab->NotifyNavigationStateChanged(
        content::INVALIDATE_TYPE_PAGE_ACTIONS);
  }
  virtual void Revert(const std::string& extension_id,
                      const base::Time& extension_install_time,
                      ApplyInfo* apply_info) const OVERRIDE {
    if (ExtensionAction* action =
            GetPageAction(apply_info->profile, extension_id)) {
      action->UndoDeclarativeShow(ExtensionTabUtil::GetTabId(apply_info->tab));
      apply_info->tab->NotifyNavigationStateChanged(
          content::INVALIDATE_TYPE_PAGE_ACTIONS);
    }
  }

 private:
  static ExtensionAction* GetPageAction(Profile* profile,
                                        const std::string& extension_id) {
    ExtensionService* service =
        ExtensionSystem::Get(profile)->extension_service();
    const Extension* extension = service->GetInstalledExtension(extension_id);
    if (!extension)
      return NULL;
    return ExtensionActionManager::Get(profile)->GetPageAction(*extension);
  }
  virtual ~ShowPageAction() {}

  DISALLOW_COPY_AND_ASSIGN(ShowPageAction);
};

struct ContentActionFactory {
  // Factory methods for ContentAction instances. |extension| is the extension
  // for which the action is being created. |dict| contains the json dictionary
  // that describes the action. |error| is used to return error messages in case
  // the extension passed an action that was syntactically correct but
  // semantically incorrect. |bad_message| is set to true in case |dict| does
  // not confirm to the validated JSON specification.
  typedef scoped_refptr<ContentAction>(*FactoryMethod)(
      const Extension* /* extension */,
      const base::DictionaryValue* /* dict */,
      std::string* /* error */,
      bool* /* bad_message */);
  // Maps the name of a declarativeContent action type to the factory
  // function creating it.
  std::map<std::string, FactoryMethod> factory_methods;

  ContentActionFactory() {
    factory_methods[keys::kShowPageAction] =
        &ShowPageAction::Create;
  }
};

base::LazyInstance<ContentActionFactory>::Leaky
    g_content_action_factory = LAZY_INSTANCE_INITIALIZER;

}  // namespace

//
// ContentAction
//

ContentAction::ContentAction() {}

ContentAction::~ContentAction() {}

// static
scoped_refptr<ContentAction> ContentAction::Create(
    const Extension* extension,
    const base::Value& json_action,
    std::string* error,
    bool* bad_message) {
  *error = "";
  *bad_message = false;

  const base::DictionaryValue* action_dict = NULL;
  INPUT_FORMAT_VALIDATE(json_action.GetAsDictionary(&action_dict));

  std::string instance_type;
  INPUT_FORMAT_VALIDATE(
      action_dict->GetString(keys::kInstanceType, &instance_type));

  ContentActionFactory& factory = g_content_action_factory.Get();
  std::map<std::string, ContentActionFactory::FactoryMethod>::iterator
      factory_method_iter = factory.factory_methods.find(instance_type);
  if (factory_method_iter != factory.factory_methods.end())
    return (*factory_method_iter->second)(
        extension, action_dict, error, bad_message);

  *error = base::StringPrintf(kInvalidInstanceTypeError, instance_type.c_str());
  return scoped_refptr<ContentAction>();
}

}  // namespace extensions