Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/apps/system/js/permission_manager.js
blob: f3c013c6e11c09f8db18434aa645cef50911c3e8 (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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */

'use strict';

var PermissionManager = (function() {
  var _ = navigator.mozL10n.get;

  window.addEventListener('mozChromeEvent', function pm_chromeEventHandler(e) {
    var detail = e.detail;
    switch (detail.type) {
      case 'permission-prompt':
        overlay.dataset.type = detail.permission;
        handlePermissionPrompt(detail);
        break;
      case 'fullscreenoriginchange':
        delete overlay.dataset.type;
        handleFullscreenOriginChange(detail);
        break;
    }
  });

  var fullscreenRequest = undefined;

  var handleFullscreenOriginChange = function(detail) {
    // If there's already a fullscreen request visible, cancel it,
    // we'll show the request for the new domain.
    if (fullscreenRequest != undefined) {
      cancelRequest(fullscreenRequest);
      fullscreenRequest = undefined;
    }
    if (detail.fullscreenorigin != WindowManager.getDisplayedApp()) {
      // The message to be displayed on the approval UI.
      var message = _('fullscreen-request', { 'origin': detail.fullscreenorigin });
      fullscreenRequest = requestPermission(message,
                                            /* yesCallback */ null,
                                            /* noCallback */ function() {
                                              document.mozCancelFullScreen();
                                            });
    }
  };

  var handlePermissionPrompt = function pm_handlePermissionPrompt(detail) {
    remember.checked = detail.remember ? true : false;
    var str = '';

    var permissionID = 'perm-' + detail.permission.replace(':', '-');
    if (detail.isApp) { // App
      str = _(permissionID + '-appRequest', { 'app': detail.appName });
    } else { // Web content
      str = _(permissionID + '-webRequest', { 'site': detail.origin });
    }

    requestPermission(str, function pm_permYesCB() {
      dispatchResponse(detail.id, 'permission-allow', remember.checked);
    }, function pm_permNoCB() {
      dispatchResponse(detail.id, 'permission-deny', remember.checked);
    });
  };

  var dispatchResponse = function pm_dispatchResponse(id, type, remember) {
    var event = document.createEvent('CustomEvent');
    remember = remember ? true : false;

    event.initCustomEvent('mozContentEvent', true, true, {
      id: id,
      type: type,
      remember: remember
    });
    window.dispatchEvent(event);
  };

  // A queue of pending requests. Callers of requestPermission() must be
  // careful not to create an infinite loop!
  var pending = [];

  // Div over in which the permission UI resides.
  var overlay = document.getElementById('permission-screen');
  var dialog = document.getElementById('permission-dialog');
  var message = document.getElementById('permission-message');

  // "Yes"/"No" buttons on the permission UI.
  var yes = document.getElementById('permission-yes');
  var no = document.getElementById('permission-no');

  // Remember the choice checkbox
  var remember = document.getElementById('permission-remember-checkbox');
  var rememberSection = document.getElementById('permission-remember-section');

  // The ID of the next permission request. This is incremented by one
  // on every request, modulo some large number to prevent overflow problems.
  var nextRequestID = 0;

  // The ID of the request currently visible on the screen. This has the value
  // "undefined" when there is no request visible on the screen.
  var currentRequestId = undefined;

  var hidePermissionPrompt = function() {
    overlay.classList.remove('visible');
    currentRequestId = undefined;
    // Cleanup the event handlers.
    yes.removeEventListener('click', clickHandler);
    yes.callback = null;
    no.removeEventListener('click', clickHandler);
    no.callback = null;
  };

  // Show the next request, if we have one.
  var showNextPendingRequest = function() {
    if (pending.length == 0)
      return;
    var request = pending.shift();
    showPermissionPrompt(request.id,
                         request.message,
                         request.yescallback,
                         request.nocallback);
  };

  // This is the event listener function for the yes/no buttons.
  var clickHandler = function(evt) {
    var callback = null;
    if (evt.target === yes && yes.callback) {
      callback = yes.callback;
    } else if (evt.target === no && no.callback) {
      callback = no.callback;
    }
    hidePermissionPrompt();

    // Call the appropriate callback, if it is defined.
    if (callback)
      window.setTimeout(callback, 0);

    showNextPendingRequest();
  };

  var requestPermission = function(msg,
                                   yescallback, nocallback) {
    var id = nextRequestID;
    nextRequestID = (nextRequestID + 1) % 1000000;

    if (currentRequestId != undefined) {
      // There is already a permission request being shown, queue this one.
      pending.push({
        id: id,
        message: msg,
        yescallback: yescallback,
        nocallback: nocallback
      });
      return id;
    }

    showPermissionPrompt(id, msg, yescallback, nocallback);

    return id;
  };

  var showPermissionPrompt = function(id, msg,
                                      yescallback, nocallback) {
    // Put the message in the dialog.
    // Note plain text since this may include text from
    // untrusted app manifests, for example.
    message.textContent = msg;

    currentRequestId = id;

    // Make the screen visible
    overlay.classList.add('visible');

    // Set event listeners for the yes and no buttons
    yes.addEventListener('click', clickHandler);
    yes.callback = yescallback;

    no.addEventListener('click', clickHandler);
    no.callback = nocallback;
  };

  // Cancels a request with a specfied id. Request can either be
  // currently showing, or pending. If there are further pending requests,
  // the next is shown.
  var cancelRequest = function(id) {
    if (currentRequestId === id) {
      // Request is currently being displayed. Hide the permission prompt,
      // and show the next request, if we have any.
      hidePermissionPrompt();
      showNextPendingRequest();
    } else {
      // The request is currently not being displayed. Search through the
      // list of pending requests, and remove it from the list if present.
      for (var i = 0; i < pending.length; i++) {
        if (pending[i].id === id) {
          pending.splice(i, 1);
          break;
        }
      }
    }
  };

  rememberSection.addEventListener('click', function onLabelClick() {
    remember.checked = !remember.checked;
  });

}());