view components/src/nsJSWeakRef.cpp @ 3:ac0c8fe4aec1

Fixed a bug that Linux's gcc didn't like re: too many semicolons.
author Atul Varma <varmaa@toolness.com>
date Wed, 22 Apr 2009 08:48:38 -0700
parents 496cd9ab2298
children
line wrap: on
line source

/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is JSWeakRef.
 *
 * The Initial Developer of the Original Code is Mozilla.
 * Portions created by the Initial Developer are Copyright (C) 2007
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Atul Varma <atul@mozilla.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "nsJSWeakRef.h"

#include "jsapi.h"
#include "nsIXPConnect.h"
#include "nsAXPCNativeCallContext.h"
#include "nsServiceManagerUtils.h"
#include "nsComponentManagerUtils.h"

class nsJSWeakRefListNode {
public:
  nsJSWeakRef *data;
  nsJSWeakRefListNode *next;
};

class nsJSWeakRefImpl {
public:
  JSObject *weakRef;
  JSContext *weakCx;
  nsJSWeakRefListNode *node;
};

static nsJSWeakRefListNode *gList;
static JSGCCallback gOldJSGCCallback;

void processGarbage() {
  nsJSWeakRefListNode *node = gList;
  nsJSWeakRefListNode *prevNode = nsnull;
  nsJSWeakRefListNode *nextNode = nsnull;
  while (node) {
    nextNode = node->next;
    if (node->data) {
      if (JS_IsAboutToBeFinalized(node->data->impl->weakCx,
                                  node->data->impl->weakRef)) {
        // Tell the weak reference holder that its target no longer exists.
        node->data->impl->weakRef = nsnull;
        node->data->impl->weakCx = nsnull;
        node->data->impl->node = nsnull;

        // Delete this node.
        if (prevNode)
          prevNode->next = nextNode;
        else
          gList = nextNode;
        delete node;
      } else
        // This is our general case; just move on to the next node.
        prevNode = node;
    } else {
      // The weak reference holder went away, so just delete this node.
      if (prevNode)
        prevNode->next = nextNode;
      else
        gList = nextNode;
      delete node;
    }
    node = nextNode;
  }
}

static JSBool XPCCycleCollectGCCallback(JSContext *cx, JSGCStatus status) {
  if (status == JSGC_MARK_END)
    processGarbage();
  return gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE;
}

nsJSWeakRef::nsJSWeakRef()
{
  this->impl = new nsJSWeakRefImpl();
  this->impl->weakRef = nsnull;
  this->impl->weakCx = nsnull;
  this->impl->node = nsnull;
}

nsJSWeakRef::~nsJSWeakRef()
{
  if (this->impl->node)
    // Tell the GC callback to remove our node from the list next time around.
    this->impl->node->data = nsnull;

  delete this->impl;
  this->impl = nsnull;
}

NS_IMETHODIMP nsJSWeakRef::Set()
{
  nsresult rv = NS_OK;
  nsCOMPtr<nsIXPConnect> xpc = do_GetService(
    "@mozilla.org/js/xpc/XPConnect;1",
    &rv
  );
  if (NS_FAILED(rv))
    return NS_ERROR_FAILURE;

  // get the xpconnect native call context
  nsAXPCNativeCallContext *cc = nsnull;
  xpc->GetCurrentNativeCallContext(&cc);
  if(!cc)
    return NS_ERROR_FAILURE;

  // Get JSContext of current call
  JSContext* cx;
  rv = cc->GetJSContext(&cx);
  if(NS_FAILED(rv) || !cx)
    return NS_ERROR_FAILURE;

  // get place for return value
  jsval *rval = nsnull;
  rv = cc->GetRetValPtr(&rval);
  if(NS_FAILED(rv) || !rval)
    return NS_ERROR_FAILURE;

  // get argc and argv and verify arg count
  PRUint32 argc;
  rv = cc->GetArgc(&argc);
  if(NS_FAILED(rv))
    return rv;

  if (argc < 1)
    return NS_ERROR_XPC_NOT_ENOUGH_ARGS;

  jsval *argv;
  rv = cc->GetArgvPtr(&argv);
  if (NS_FAILED(rv))
    return rv;

  if (!JSVAL_IS_OBJECT(argv[0]))
    return NS_ERROR_ILLEGAL_VALUE;

  this->impl->weakRef = JSVAL_TO_OBJECT(argv[0]);
  this->impl->weakCx = cx;

  // Insert a new node at the head of the global list.
  nsJSWeakRefListNode *newNode = new nsJSWeakRefListNode();
  newNode->data = this;
  newNode->next = gList;
  this->impl->node = newNode;
  gList = newNode;

  // TODO: Note that this is never removed.
  if (!gOldJSGCCallback)
    gOldJSGCCallback = JS_SetGCCallback(cx, XPCCycleCollectGCCallback);

  return NS_OK;
}

NS_IMETHODIMP nsJSWeakRef::Get()
{
  nsresult rv = NS_OK;
  nsCOMPtr<nsIXPConnect> xpc = do_GetService(
    "@mozilla.org/js/xpc/XPConnect;1",
    &rv
  );
  if (NS_FAILED(rv))
    return NS_ERROR_FAILURE;

  // get the xpconnect native call context
  nsAXPCNativeCallContext *cc = nsnull;
  xpc->GetCurrentNativeCallContext(&cc);
  if(!cc)
    return NS_ERROR_FAILURE;

  // get place for return value
  jsval *rval = nsnull;
  rv = cc->GetRetValPtr(&rval);
  if(NS_FAILED(rv) || !rval)
    return NS_ERROR_FAILURE;

  // TODO: Do we have to increase the reference count of the object
  // or anything?

  // This automatically is set to JSVAL_NULL if weakRef is nsnull.
  *rval = OBJECT_TO_JSVAL(this->impl->weakRef);
  cc->SetReturnValueWasSet(PR_TRUE);

  return NS_OK;
}

NS_IMPL_ISUPPORTS1(nsJSWeakRef, nsIJSWeakRef)