onBuildPreview: makeInfallible(function(actor, grip, rawObj) {
    if (!DevToolsUtils.getProperty(actor.obj, "jquery")) {
      return false;
    }

    let length = DevToolsUtils.getProperty(actor.obj, "length");
    if (typeof length != "number") {
      return false;
    }

    let preview = grip.preview = {
      kind: "jQuery",
      length: length,
    };

    if (actor.threadActor._gripDepth > 1) {
      return true;
    }

    Trace.sysout("FireQueryActor.onBuildPreview;", arguments);

    preview.items = [];

    let raw = actor.obj.unsafeDereference();
    for (let i = 0; i < preview.length; ++i) {
      // Array Xrays filter out various possibly-unsafe properties (like
      // functions, and claim that the value is undefined instead. This
      // is generally the right thing for privileged code accessing untrusted
      // objects, but quite confusing for Object previews. So we manually
      // override this protection by waiving Xrays on the array, and re-applying
      // Xrays on any indexed value props that we pull off of it.
      let desc = Object.getOwnPropertyDescriptor(Cu.waiveXrays(raw), i);
      if (desc && !desc.get && !desc.set) {
        let value = Cu.unwaiveXrays(desc.value);
        value = makeDebuggeeValueIfNeeded(actor.obj, value);

        let grip = actor.threadActor.createValueGrip(value);
        preview.items.push(grip);

        let data = hasJQueryData(desc.value);
        if (data) {
          data = Cu.unwaiveXrays(data);
          data = makeDebuggeeValueIfNeeded(actor.obj, data);
          grip.preview.jQueryData = actor.threadActor.createValueGrip(data);

          // xxxHonza: generate preview for the data?
        }
      } else {
        preview.items.push(null);
      }

      if (preview.length == OBJECT_PREVIEW_MAX_ITEMS) {
        break;
      }
    }

    Trace.sysout("FireQueryActor.onPreview; jQuery preview", preview);

    return true;
  }),
Example #2
0
function enumWeakSetEntries(objectActor) {
  // We currently lack XrayWrappers for WeakSet, so when we iterate over
  // the values, the temporary iterator objects get created in the target
  // compartment. However, we _do_ have Xrays to Object now, so we end up
  // Xraying those temporary objects, and filtering access to |it.value|
  // based on whether or not it's Xrayable and/or callable, which breaks
  // the for/of iteration.
  //
  // This code is designed to handle untrusted objects, so we can safely
  // waive Xrays on the iterable, and relying on the Debugger machinery to
  // make sure we handle the resulting objects carefully.
  const raw = objectActor.obj.unsafeDereference();
  const basekeys = ChromeUtils.nondeterministicGetWeakSetKeys(raw);
  const keys = isWorker ? basekeys : Cu.waiveXrays(basekeys);

  return {
    [Symbol.iterator]: function* () {
      for (const item of keys) {
        yield gripFromEntry(objectActor, item);
      }
    },
    size: keys.length,
    propertyName(index) {
      return index;
    },
    propertyDescription(index) {
      const val = keys[index];
      return {
        enumerable: true,
        value: gripFromEntry(objectActor, val),
      };
    },
  };
}
Example #3
0
// Exports variables that will be accessed by the non-privileged scripts.
function exportData(win, headers) {
  const json = new win.Text();
  const JSONView = Cu.cloneInto({
    debugJsModules,
    headers,
    json,
    readyState: "uninitialized",
    Locale: {
      $STR: key => {
        try {
          return jsonViewStrings.GetStringFromName(key);
        } catch (err) {
          console.error(err);
          return undefined;
        }
      },
    },
  }, win, {
    cloneFunctions: true,
    wrapReflectors: true,
  });
  try {
    Object.defineProperty(Cu.waiveXrays(win), "JSONView", {
      value: JSONView,
      configurable: true,
      enumerable: true,
      writable: true,
    });
  } catch (error) {
    Cu.reportError(error);
  }
  return {json};
}
Example #4
0
function enumMapEntries(objectActor) {
  // Iterating over a Map via .entries goes through various intermediate
  // objects - an Iterator object, then a 2-element Array object, then the
  // actual values we care about. We don't have Xrays to Iterator objects,
  // so we get Opaque wrappers for them. And even though we have Xrays to
  // Arrays, the semantics often deny access to the entires based on the
  // nature of the values. So we need waive Xrays for the iterator object
  // and the tupes, and then re-apply them on the underlying values until
  // we fix bug 1023984.
  //
  // Even then though, we might want to continue waiving Xrays here for the
  // same reason we do so for Arrays above - this filtering behavior is likely
  // to be more confusing than beneficial in the case of Object previews.
  let keys, getValue;
  if (isWorker) {
    const keysIterator = DevToolsUtils.callPropertyOnObject(objectActor.obj, "keys");
    keys = [...DevToolsUtils.makeDebuggeeIterator(keysIterator)];
    const valuesIterator = DevToolsUtils.callPropertyOnObject(objectActor.obj, "values");
    const values = [...DevToolsUtils.makeDebuggeeIterator(valuesIterator)];
    const map = new Map();
    for (let i = 0; i < keys.length; i++) {
      map.set(keys[i], values[i]);
    }
    getValue = key => map.get(key);
  } else {
    const raw = objectActor.obj.unsafeDereference();
    keys = [...Cu.waiveXrays(Map.prototype.keys.call(raw))];
    getValue = key => Map.prototype.get.call(raw, key);
  }

  return {
    [Symbol.iterator]: function* () {
      for (const key of keys) {
        const value = getValue(key);
        yield [ key, value ].map(val => gripFromEntry(objectActor, val));
      }
    },
    size: keys.length,
    propertyName(index) {
      return index;
    },
    propertyDescription(index) {
      const key = keys[index];
      const val = getValue(key);
      return {
        enumerable: true,
        value: {
          type: "mapEntry",
          preview: {
            key: gripFromEntry(objectActor, key),
            value: gripFromEntry(objectActor, val),
          },
        },
      };
    },
  };
}
Example #5
0
WebConsoleCommands._registerOriginal("print", function (owner, value) {
  owner.helperResult = { rawOutput: true };
  if (typeof value === "symbol") {
    return Symbol.prototype.toString.call(value);
  }
  // Waiving Xrays here allows us to see a closer representation of the
  // underlying object. This may execute arbitrary content code, but that
  // code will run with content privileges, and the result will be rendered
  // inert by coercing it to a String.
  return String(Cu.waiveXrays(value));
});
Example #6
0
function enumWeakMapEntries(objectActor) {
  // We currently lack XrayWrappers for WeakMap, so when we iterate over
  // the values, the temporary iterator objects get created in the target
  // compartment. However, we _do_ have Xrays to Object now, so we end up
  // Xraying those temporary objects, and filtering access to |it.value|
  // based on whether or not it's Xrayable and/or callable, which breaks
  // the for/of iteration.
  //
  // This code is designed to handle untrusted objects, so we can safely
  // waive Xrays on the iterable, and relying on the Debugger machinery to
  // make sure we handle the resulting objects carefully.
  const raw = objectActor.obj.unsafeDereference();
  const basekeys = ChromeUtils.nondeterministicGetWeakMapKeys(raw);
  const keys = isWorker ? basekeys : Cu.waiveXrays(basekeys);

  const values = [];
  if (isWorker) {
    for (const k of keys) {
      const nk = ObjectUtils.makeDebuggeeValueIfNeeded(objectActor.obj, k);
      const v = DevToolsUtils.callPropertyOnObject(objectActor.obj, "get", nk);
      values.push(ObjectUtils.unwrapDebuggeeValue(v));
    }
  } else {
    for (const k of keys) {
      values.push(WeakMap.prototype.get.call(raw, k));
    }
  }

  return {
    [Symbol.iterator]: function* () {
      for (let i = 0; i < keys.length; i++) {
        yield [ keys[i], values[i] ].map(val => gripFromEntry(objectActor, val));
      }
    },
    size: keys.length,
    propertyName(index) {
      return index;
    },
    propertyDescription(index) {
      const key = keys[index];
      const val = values[index];
      return {
        enumerable: true,
        value: {
          type: "mapEntry",
          preview: {
            key: gripFromEntry(objectActor, key),
            value: gripFromEntry(objectActor, val),
          },
        },
      };
    },
  };
}
Example #7
0
        get: function(...args) {
          if (!originalGetter) return undefined;
          let result = Cu.waiveXrays(originalGetter.apply(this, args));

          if (self._recording) {
            let type = CallWatcherFront.GETTER_FUNCTION;
            let stack = getStack(name);
            let timestamp = self.tabActor.window.performance.now() - self._timestampEpoch;
            callback(unwrappedWindow, global, this, type, name, stack, timestamp, args, result);
          }
          return result;
        },
Example #8
0
WebConsoleCommands._registerOriginal("values", function (owner, object) {
  let values = [];
  // Need to waive Xrays so we can iterate functions and accessor properties
  let waived = Cu.waiveXrays(object);
  let names = Object.getOwnPropertyNames(waived);

  for (let name of names) {
    values.push(waived[name]);
  }

  return Cu.cloneInto(values, owner.window);
});
Example #9
0
      Cu.exportFunction(function(...args) {
        let result;
        try {
          result = Cu.waiveXrays(originalFunc.apply(this, args));
        } catch (e) {
          throw createContentError(e, unwrappedWindow);
        }

        if (self._recording) {
          let type = CallWatcherFront.METHOD_FUNCTION;
          let stack = getStack(name);
          let timestamp = self.tabActor.window.performance.now() - self._timestampEpoch;
          callback(unwrappedWindow, global, this, type, name, stack, timestamp, args, result);
        }
        return result;
      }, target, { defineAs: name });
Example #10
0
  Array: [function({obj, hooks}, grip) {
    const length = ObjectUtils.getArrayLength(obj);

    grip.preview = {
      kind: "ArrayLike",
      length: length,
    };

    if (hooks.getGripDepth() > 1) {
      return true;
    }

    const raw = obj.unsafeDereference();
    const items = grip.preview.items = [];

    for (let i = 0; i < length; ++i) {
      if (raw) {
        // Array Xrays filter out various possibly-unsafe properties (like
        // functions, and claim that the value is undefined instead. This
        // is generally the right thing for privileged code accessing untrusted
        // objects, but quite confusing for Object previews. So we manually
        // override this protection by waiving Xrays on the array, and re-applying
        // Xrays on any indexed value props that we pull off of it.
        const desc = Object.getOwnPropertyDescriptor(Cu.waiveXrays(raw), i);
        if (desc && !desc.get && !desc.set) {
          let value = Cu.unwaiveXrays(desc.value);
          value = ObjectUtils.makeDebuggeeValueIfNeeded(obj, value);
          items.push(hooks.createValueGrip(value));
        } else {
          items.push(null);
        }
      } else {
        // When recording/replaying we don't have a raw object, but also don't
        // need to deal with Xrays into the debuggee compartment.
        const value = DevToolsUtils.getProperty(obj, i);
        items.push(hooks.createValueGrip(value));
      }

      if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
        break;
      }
    }

    return true;
  }],
Example #11
0
    context[funcName] = function (...glArgs) {
      if (timing <= 0 && !observer.suppressHandlers) {
        let glBreak = observer[beforeFuncName](glArgs, cache, proxy);
        if (glBreak) {
          return undefined;
        }
      }

      // Invoking .apply on an unxrayed content function doesn't work, because
      // the arguments array is inaccessible to it. Get Xrays back.
      let glResult = Cu.waiveXrays(Cu.unwaiveXrays(originalFunc).apply(this, glArgs));

      if (timing >= 0 && !observer.suppressHandlers) {
        let glBreak = observer[afterFuncName](glArgs, glResult, cache, proxy);
        if (glBreak) {
          return undefined;
        }
      }

      return glResult;
    };
Example #12
0
function enumSetEntries(objectActor) {
  // We currently lack XrayWrappers for Set, so when we iterate over
  // the values, the temporary iterator objects get created in the target
  // compartment. However, we _do_ have Xrays to Object now, so we end up
  // Xraying those temporary objects, and filtering access to |it.value|
  // based on whether or not it's Xrayable and/or callable, which breaks
  // the for/of iteration.
  //
  // This code is designed to handle untrusted objects, so we can safely
  // waive Xrays on the iterable, and relying on the Debugger machinery to
  // make sure we handle the resulting objects carefully.
  let values;
  if (isWorker) {
    const iterator = DevToolsUtils.callPropertyOnObject(objectActor.obj, "values");
    values = [...DevToolsUtils.makeDebuggeeIterator(iterator)];
  } else {
    const raw = objectActor.obj.unsafeDereference();
    values = [...Cu.waiveXrays(Set.prototype.values.call(raw))];
  }

  return {
    [Symbol.iterator]: function* () {
      for (const item of values) {
        yield gripFromEntry(objectActor, item);
      }
    },
    size: values.length,
    propertyName(index) {
      return index;
    },
    propertyDescription(index) {
      const val = values[index];
      return {
        enumerable: true,
        value: gripFromEntry(objectActor, val),
      };
    },
  };
}
Example #13
0
  getAllocations: expectState("attached", function() {
    if (this.dbg.memory.allocationsLogOverflowed) {
      // Since the last time we drained the allocations log, there have been
      // more allocations than the log's capacity, and we lost some data. There
      // isn't anything actionable we can do about this, but put a message in
      // the browser console so we at least know that it occurred.
      reportException("MemoryBridge.prototype.getAllocations",
                      "Warning: allocations log overflowed and lost some data.");
    }

    const allocations = this.dbg.memory.drainAllocationsLog();
    const packet = {
      allocations: [],
      allocationsTimestamps: [],
      allocationSizes: [],
    };
    for (const { frame: stack, timestamp, size } of allocations) {
      if (stack && Cu.isDeadWrapper(stack)) {
        continue;
      }

      // Safe because SavedFrames are frozen/immutable.
      const waived = Cu.waiveXrays(stack);

      // Ensure that we have a form, size, and index for new allocations
      // because we potentially haven't seen some or all of them yet. After this
      // loop, we can rely on the fact that every frame we deal with already has
      // its metadata stored.
      const index = this._frameCache.addFrame(waived);

      packet.allocations.push(index);
      packet.allocationsTimestamps.push(timestamp);
      packet.allocationSizes.push(size);
    }

    return this._frameCache.updateFramePacket(packet);
  }, "getting allocations"),
Example #14
0
WebConsoleCommands._registerOriginal("keys", function JSTH_keys(aOwner, aObject)
{
  // Need to waive Xrays so we can iterate functions and accessor properties
  return Cu.cloneInto(Object.keys(Cu.waiveXrays(aObject)), aOwner.window);
});
Example #15
0
  initialize: function WorkerSandbox(worker, window) {
    let model = {};
    sandboxes.set(this, model);
    model.worker = worker;
    // We receive a wrapped window, that may be an xraywrapper if it's content
    let proto = window;

    // TODO necessary?
    // Ensure that `emit` has always the right `this`
    this.emit = this.emit.bind(this);
    this.emitSync = this.emitSync.bind(this);

    // Use expanded principal for content-script if the content is a
    // regular web content for better isolation.
    // (This behavior can be turned off for now with the unsafe-content-script
    // flag to give addon developers time for making the necessary changes)
    // But prevent it when the Worker isn't used for a content script but for
    // injecting `addon` object into a Panel scope, for example.
    // That's because:
    // 1/ It is useless to use multiple domains as the worker is only used
    // to communicate with the addon,
    // 2/ By using it it would prevent the document to have access to any JS
    // value of the worker. As JS values coming from multiple domain principals
    // can't be accessed by 'mono-principals' (principal with only one domain).
    // Even if this principal is for a domain that is specified in the multiple
    // domain principal.
    let principals = window;
    let wantGlobalProperties = [];
    let isSystemPrincipal = secMan.isSystemPrincipal(
      window.document.nodePrincipal);
    if (!isSystemPrincipal && !requiresAddonGlobal(worker)) {
      if (EXPANDED_PRINCIPALS.length > 0) {
        // We have to replace XHR constructor of the content document
        // with a custom cross origin one, automagically added by platform code:
        delete proto.XMLHttpRequest;
        wantGlobalProperties.push('XMLHttpRequest');
      }
      if (!waiveSecurityMembrane)
        principals = EXPANDED_PRINCIPALS.concat(window);
    }

    // Create the sandbox and bind it to window in order for content scripts to
    // have access to all standard globals (window, document, ...)
    let content = sandbox(principals, {
      sandboxPrototype: proto,
      wantXrays: !requiresAddonGlobal(worker),
      wantGlobalProperties: wantGlobalProperties,
      wantExportHelpers: true,
      sameZoneAs: window,
      metadata: {
        SDKContentScript: true,
        'inner-window-id': getInnerId(window)
      }
    });
    model.sandbox = content;

    // We have to ensure that window.top and window.parent are the exact same
    // object than window object, i.e. the sandbox global object. But not
    // always, in case of iframes, top and parent are another window object.
    let top = window.top === window ? content : content.top;
    let parent = window.parent === window ? content : content.parent;
    merge(content, {
      // We need 'this === window === top' to be true in toplevel scope:
      get window() {
        return content;
      },
      get top() {
        return top;
      },
      get parent() {
        return parent;
      }
    });

    // Use the Greasemonkey naming convention to provide access to the
    // unwrapped window object so the content script can access document
    // JavaScript values.
    // NOTE: this functionality is experimental and may change or go away
    // at any time!
    //
    // Note that because waivers aren't propagated between origins, we
    // need the unsafeWindow getter to live in the sandbox.
    var unsafeWindowGetter =
      new content.Function('return window.wrappedJSObject || window;');
    Object.defineProperty(content, 'unsafeWindow', {get: unsafeWindowGetter});

    // Load trusted code that will inject content script API.
    let ContentWorker = load(content, CONTENT_WORKER_URL);

    // prepare a clean `self.options`
    let options = 'contentScriptOptions' in worker ?
      JSON.stringify(worker.contentScriptOptions) :
      undefined;

    // Then call `inject` method and communicate with this script
    // by trading two methods that allow to send events to the other side:
    //   - `onEvent` called by content script
    //   - `result.emitToContent` called by addon script
    let onEvent = Cu.exportFunction(onContentEvent.bind(null, this), ContentWorker);
    let chromeAPI = createChromeAPI(ContentWorker);
    let result = Cu.waiveXrays(ContentWorker).inject(content, chromeAPI, onEvent, options);

    // Merge `emitToContent` into our private model of the
    // WorkerSandbox so we can communicate with content script
    model.emitToContent = result;

    let console = new PlainTextConsole(null, getInnerId(window));

    // Handle messages send by this script:
    setListeners(this, console);

    // Inject `addon` global into target document if document is trusted,
    // `addon` in document is equivalent to `self` in content script.
    if (requiresAddonGlobal(worker)) {
      Object.defineProperty(getUnsafeWindow(window), 'addon', {
          value: content.self,
          configurable: true
        }
      );
    }

    // Inject our `console` into target document if worker doesn't have a tab
    // (e.g Panel, PageWorker).
    // `worker.tab` can't be used because bug 804935.
    if (!isWindowInTab(window)) {
      let win = getUnsafeWindow(window);

      // export our chrome console to content window, as described here:
      // https://developer.mozilla.org/en-US/docs/Components.utils.createObjectIn
      let con = Cu.createObjectIn(win);

      let genPropDesc = function genPropDesc(fun) {
        return { enumerable: true, configurable: true, writable: true,
          value: console[fun] };
      }

      const properties = {
        log: genPropDesc('log'),
        info: genPropDesc('info'),
        warn: genPropDesc('warn'),
        error: genPropDesc('error'),
        debug: genPropDesc('debug'),
        trace: genPropDesc('trace'),
        dir: genPropDesc('dir'),
        group: genPropDesc('group'),
        groupCollapsed: genPropDesc('groupCollapsed'),
        groupEnd: genPropDesc('groupEnd'),
        time: genPropDesc('time'),
        timeEnd: genPropDesc('timeEnd'),
        profile: genPropDesc('profile'),
        profileEnd: genPropDesc('profileEnd'),
        exception: genPropDesc('exception'),
        assert: genPropDesc('assert'),
        count: genPropDesc('count'),
        table: genPropDesc('table'),
        clear: genPropDesc('clear'),
        dirxml: genPropDesc('dirxml'),
        markTimeline: genPropDesc('markTimeline'),
        timeline: genPropDesc('timeline'),
        timelineEnd: genPropDesc('timelineEnd'),
        timeStamp: genPropDesc('timeStamp'),
      };

      Object.defineProperties(con, properties);
      Cu.makeObjectPropsNormal(con);

      win.console = con;
    };

    emit(events, "content-script-before-inserted", {
      window: window,
      worker: worker
    });

    // The order of `contentScriptFile` and `contentScript` evaluation is
    // intentional, so programs can load libraries like jQuery from script URLs
    // and use them in scripts.
    let contentScriptFile = ('contentScriptFile' in worker)
          ? worker.contentScriptFile
          : null,
        contentScript = ('contentScript' in worker)
          ? worker.contentScript
          : null;

    if (contentScriptFile)
      importScripts.apply(null, [this].concat(contentScriptFile));

    if (contentScript) {
      evaluateIn(
        this,
        Array.isArray(contentScript) ? contentScript.join(';\n') : contentScript
      );
    }
  },
Example #16
0
  _pullTimelineData: function () {
    let docShells = this.docShells;
    if (!this._isRecording || !docShells.length) {
      return;
    }

    let endTime = docShells[0].now();
    let markers = [];

    // Gather markers if requested.
    if (this._withMarkers || this._withDocLoadingEvents) {
      for (let docShell of docShells) {
        for (let marker of docShell.popProfileTimelineMarkers()) {
          markers.push(marker);

          // The docshell may return markers with stack traces attached.
          // Here we transform the stack traces via the stack frame cache,
          // which lets us preserve tail sharing when transferring the
          // frames to the client.  We must waive xrays here because Firefox
          // doesn't understand that the Debugger.Frame object is safe to
          // use from chrome.  See Tutorial-Alloc-Log-Tree.md.
          if (this._withFrames) {
            if (marker.stack) {
              marker.stack = this._stackFrames.addFrame(Cu.waiveXrays(marker.stack));
            }
            if (marker.endStack) {
              marker.endStack = this._stackFrames.addFrame(
                Cu.waiveXrays(marker.endStack)
              );
            }
          }

          // Emit some helper events for "DOMContentLoaded" and "Load" markers.
          if (this._withDocLoadingEvents) {
            if (marker.name == "document::DOMContentLoaded" ||
                marker.name == "document::Load") {
              events.emit(this, "doc-loading", marker, endTime);
            }
          }
        }
      }
    }

    // Emit markers if requested.
    if (this._withMarkers && markers.length > 0) {
      events.emit(this, "markers", markers, endTime);
    }

    // Emit framerate data if requested.
    if (this._withTicks) {
      events.emit(this, "ticks", endTime, this._framerate.getPendingTicks());
    }

    // Emit memory data if requested.
    if (this._withMemory) {
      events.emit(this, "memory", endTime, this._memory.measure());
    }

    // Emit stack frames data if requested.
    if (this._withFrames && this._withMarkers) {
      let frames = this._stackFrames.makeEvent();
      if (frames) {
        events.emit(this, "frames", endTime, frames);
      }
    }

    this._dataPullTimeout = setTimeout(() => {
      this._pullTimelineData();
    }, DEFAULT_TIMELINE_DATA_PULL_TIMEOUT);
  },