XPCNativeWrapper

Obiekt XPCNativeWrapper służy do opakowania obiektu, tak aby można było uzyskać do niego dostęp w bezpieczny sposób z poziomu kodu uprzywilejowanego. Można z niego korzystać we wszystkich wersjach Firefoksa, przy czym jego działanie zostało nieco zmienione w wersji 1.5 (Gecko 1.8) i nowszych. Informacje na temat działania obiektu XPCNativeWrapper w wersjach Firefoksa starszych niż 1.5 można znaleźć w artykule XPCNativeWrapper w bazie wiedzy witryny MozillaZine. W niniejszym dokumencie opisano działanie obiektu XPCNativeWrapper w programie Firefox w wersji 1.5 i nowszych.

Jak działa obiekt XPCNativeWrapper

Obiekt XPCNativeWrapper ogranicza dostęp do własności i metod obiektu, który jest przez niego opakowany. Za pośrednictwem obiektu XPCNativeWrapper dostępne są tylko te własności i metody, które zdefiniowano w specyfikacji IDL (Interface Definition Language, język definicji interfejsów) lub w specyfikacji DOM Level 0 (przy czym niektóre własności i metody DOM Level 0 nie działają w odniesieniu do obiektu XPCNativeWrapper). Obiekt XPCNativeWrapper nie udostępnia przede wszystkim własności dodanych do opakowanego obiektu za pomocą kodu JavaScript ani metod pobierania i ustawiania zdefiniowanych za pomocą metod __defineGetter__ oraz __defineSetter__. W rezultacie możliwy jest bezpieczny dostęp do metod obiektu zdefiniowanych w specyfikacji IDL.

Należy koniecznie zapoznać się z sekcją Znane błędy, zwłaszcza w przypadku pisania kodu przeznaczonego do działania w wydaniach Firefoksa z serii 1.5.0.x.

Typy obiektu XPCNativeWrapper

W programie Firefox 1.5 istnieją trzy typy obiektu XPCNativeWrapper. Wszystkie służą do opakowania potencjalnie niezabezpieczonych obiektów i umożliwiają bezpieczny dostęp do własności i metod tych obiektów.

O różnicach w działaniu między trzema typami obiektu XPCNativeWrapper decydują dwie cechy, jakie może mieć obiekt opakowujący XPCNativeWrapper. Obiekt XPCNativeWrapper może być jawny (lub odwrotnie: niejawny) albo też głęboki (lub odwrotnie: płytki). Typ tworzonego obiektu opakowującego jest określany przez sposób jego utworzenia:

Utworzony przez Jawny/niejawny Głęboki/płytki
Chroniony skrypt uzyskujący dostęp do niezaufanego obiektu Niejawny Głęboki
Konstruktor wywołany z argumentami typu String Jawny Płytki
Konstruktor wywołany bez argumentów typu String Jawny Głęboki

Obiekty jawne (explicit) i niejawne (implicit)

Różnica w działaniu jawnych i niejawnych obiektów XPCNativeWrapper polega na dostępie do własności opakowanego obiektu: w przypadku obiektów niejawnych dostęp z poziomu skryptu, który nie jest chroniony, NIE jest bezpieczny. Żądania dostępu do własności opakowanego obiektu są wówczas przekazywane do własności wrappedJSObject obiektu XPCNativeWrapper.

Oznacza to, że w skryptach, które nie są chronione, nie trzeba zajmować się możliwością wystąpienia błędów, ponieważ do skryptów tych przekazywane są niejawne obiekty XPCNativeWrapper. Z drugiej strony w skryptach tego rodzaju należy przewidzieć możliwość niezabezpieczonego dostępu do obiektu.

Dostęp do własności jawnego obiektu XPCNativeWrapper jest zawsze bezpieczny, niezależnie od tego, czy wywołujący skrypt jest chroniony.

Obiekty głębokie (deep) i płytkie (shallow)

Różnica w działaniu głębokich i płytkich obiektów XPCNativeWrapper jest następująca: przy próbie dostępu do własności głębokiego obiektu opakowującego lub wywołaniu funkcji tego obiektu zwracana wartość jest również opakowana przez obiekt XPCNativeWrapper. Ten nowy obiekt XPCNativeWrapper jest także obiektem głębokim. Jest również jawny tylko jeżeli obiekt XPCNativeWrapper, do którego własności uzyskiwano dostęp, jest obiektem jawnym. Natomiast przy próbie dostępu do własności płytkiego obiektu opakowującego lub wywołaniu funkcji tego obiektu zwracana wartość może być obiektem niezabezpieczonym.

Załóżmy, że dla tego samego obiektu window istnieją trzy instancje obiektów XPCNativeWrapper. Nazwijmy je deepExplicitWindow, deepImplicitWindow i shallowWindow:

var doc1 = deepExplicitWindow.document;
// Zmienna doc1 jest teraz głębokim jawnym obiektem XPCNativeWrapper
// opakowującym obiekt document. Wywołanie na przykład metody
// doc1.open() jest bezpieczne.
var doc2 = deepImplicitWindow.document;
// Jeżeli w skrypcie wywołującym ustawiono parametr xpcnativewrappers=yes,
// zmienna doc2 jest teraz głębokim niejawnym obiektem XPCNativeWrapper
// opakowującym obiekt document.
// W przeciwnym przypadku zmienna doc2 jest teraz niezabezpieczonym obiektem
// document, ponieważ żądanie dostępu do własności zostało po prostu
// przekazane do niezabezpieczonego obiektu window.
var doc3 = shallowWindow.document;
// Zmienna doc3 jest teraz niezabezpieczonym obiektem document.

Tworzenie obiektów XPCNativeWrapper

Istnieją trzy różne sposoby tworzenia obiektów XPCNativeWrapper — po jednym dla każdego z trzech typów.

Dostęp do niezaufanego obiektu z poziomu chronionego skryptu

Za każdym razem, kiedy skrypt chroniony uzyskuje dostęp do obiektu niezaufanego, zwracany jest niejawny głęboki obiekt XPCNativeWrapper. Dostęp do własności takiego obiektu XPCNativeWrapper z poziomu skryptu chronionego jest bezpieczny.

Utworzony w ten sposób obiekt opakowujący pozostaje dostępny, dopóki istnieje opakowywany obiekt. Przy dwukrotnej próbie dostępu do obiektu w jednym wierszu kodu zwrócony zostanie ten sam obiekt XPCNativeWrapper.

Co to jest skrypt chroniony?

W wersjach programu Firefox od 1.5 do 1.5.0.5 ochrona skryptu lub jej brak zależy wyłącznie od identyfikatora URI skryptu. Skrypt jest chroniony tylko jeżeli jego identyfikator URI rozpoczyna się prefiksem zdefiniowanym jako chroniony; skrypty ładowane w inny sposób niż przez identyfikator URI (np. komponenty implementowane za pomocą kodu JavaScript) nie są chronione. Prefiksy chronione w programie Firefox są określone w Rejestrze Chrome.

Domyślnie wszystkie pakiety składników oprogramowania (content) są chronione. Oznacza to, że każdy identyfikator URI rozpoczynający się ciągiem "<tt>chrome://<nazwa pakietu>/content/</tt>" (niezależnie od pakietu) jest chroniony. W poszczególnych pakietach można zmienić tę konfigurację, ustawiając odpowiedni znacznik w pliku manifestu chrome.

Począwszy od wersji 1.5.0.6 Firefoksa, komponenty zaimplementowane za pomocą kodu JavaScript są skryptami chronionymi. Skrypt jest więc chroniony zarówno wówczas, gdy jest załadowany przez identyfikator URI rozpoczynający się chronionym prefiksem, jak i wtedy, gdy jest komponentem zaimplementowanym za pomocą kodu JavaScript.

Co to jest obiekt niezaufany?

Każdy obiekt może być zaufany lub niezaufany. Obiekt jest zaufany, gdy spełniony jest przynajmniej jeden z poniższych warunków:

  1. Jego rodzic (własność __parent__ w języku JavaScript) jest obiektem zaufanym.
  2. Jest to obiekt zakresu nadrzędnego w komponencie JavaScript.
  3. Jest to obiekt window okna zaufanego.

Ponieważ w przypadku wszystkich obiektów DOM w oknie ich własność __parent__ zawierać będzie obiekt window tego okna, będą one zaufane jedynie wówczas, gdy okno, w którym się znajdują, jest zaufane.

Co to jest okno zaufane?

Zaufanie do okna zależne jest od zawierającego je kontenera. Okno jest zaufane, gdy spełniony jest przynajmniej jeden z poniższych warunków:

  1. Jest to okno najwyższego poziomu (np. <xul:window>, <xul:dialog> lub identyfikator URI przekazany do argumentu <tt>-chrome</tt> wiersza polecenia).
  2. Rodzic okna jest zaufany oraz spełniony jest jeden z trzech warunków:
    1. Okno nie jest załadowane w obiekcie <xul:iframe> ani <xul:browser>.
    2. Ładujący okno obiekt <xul:iframe> lub <xul:browser> nie ma atrybutu "type".
    3. Wartość atrybutu "type" obiektu <xul:iframe> lub <xul:browser> ładującego okno nie jest równa "content" ani nie rozpoczyna się od ciągu znaków "content-".

Uwaga: to, czy okno jest zaufane, nie zależy od identyfikatora URI załadowanego w tym oknie. Dlatego jeżeli kod z poniższego przykładu zostanie wykonany w dokumencie, którego okno jest już zaufane, zostaną utworzone okna zaufane:

  • <xul:browser>
  • <xul:browser type="chrome">
  • <xul:browser type="wsciekly_pies">
  • <xul:iframe type="costam">
  • <html:iframe>
  • <html:iframe type="content">

Z kolei wykonanie poniższego kodu nie spowoduje utworzenia okien zaufanych:

  • <xul:browser type="content">
  • <xul:iframe type="content-primary">

Należy także zauważyć, że każde okno potomne okna niezaufanego automatycznie staje się niezaufane.

Co dzieje się, gdy skrypt uzyskuje dostęp do obiektu?

W poniższej tabeli opisano, co dzieje się, gdy skrypt uzyskuje dostęp do obiektu i jaka jest rola obiektu opakowującego.

Skrypt Obiekt Rezultaty
Chroniony Zaufany Nie jest tworzony obiekt opakowujący, w wyniku czego skrypt uzyskuje pełny dostęp do obiektu.
Chroniony Niezaufany Tworzony jest niejawny głęboki obiekt XPCNativeWrapper.
Niechroniony Zaufany Nie jest tworzony obiekt opakowujący, tak jak w pierwszym przypadku (skrypt chroniony i obiekt zaufany).
Niechroniony Niezaufany Nie jest tworzony obiekt opakowujący, tak jak w pierwszym przypadku (skrypt chroniony i obiekt zaufany)

Korzystanie z konstruktora obiektu XPCNativeWrapper

Jak wspomniano wyżej, domyślnie w nowszych wersjach Firefoksa obiekty XPCNativeWrapper są tworzone automatycznie. Nie ma potrzeby używania konstruktora obiektu XPCNativeWrapper, chyba że tworzony kod ma działać w starszych wersjach przeglądarki lub wyłączono obsługę obiektów XPCNativeWrapper.
Wywołanie konstruktora obiektu XPCNativeWrapper z argumentami typu String

Rozważmy przykład:

var contentWinWrapper = new XPCNativeWrapper(content,
                                             "document");

Tworzony jest niejawny płytki obiekt XPCNativeWrapper. Składnia została utrzymana ze względu na zgodność z wersjami Firefoksa wcześniejszymi niż 1.5. O ile do wszystkich własności obiektu contentWinWrapper można teraz uzyskać dostęp w bezpieczny sposób, o tyle dostęp do zwracanych przez nie wartości NIE jest bezpieczny (podobnie jak w wersjach Firefoksa wcześniejszych niż 1.5), ponieważ obiekt XPCNativeWrapper jest płytki. Aby zatem porównać tytuł dokumentu zawartości (content) z bieżącym zaznaczeniem zawartości, należy wykonać następujący kod:

var winWrapper = new XPCNativeWrapper(content, "document",
                                      "getSelection()");
var docWrapper = new XPCNativeWrapper(winWrapper.document,
                                      "title");
return docWrapper.title == winWrapper.getSelection();

podobnie jak w wersjach Firefoksa starszych niż 1.5. Należy zwrócić uwagę, że argument "getSelection()" nie jest niezbędny; jeżeli kod nie jest przeznaczony do użytku w wersjach Firefoksa starszych niż 1.5, można go usunąć. Jedynym elementem wymaganym do utworzenia tego typu obiektu XPCNativeWrapper w programie Firefox 1.5 lub nowszym jest pojedynczy argument typu String, umieszczony po opakowywanym obiekcie.

Wywołanie konstruktora obiektu XPCNativeWrapper bez argumentów typu String

Rozważmy przykład:

var contentWinWrapper = new XPCNativeWrapper(content);

Tworzony jest jawny głęboki obiekt XPCNativeWrapper. Dostęp do własności tego obiektu XPCNativeWrapper jest bezpieczny, a zwracane wartości będą także opakowane przez jawne głębokie obiekty XPCNativeWrapper.

Czas istnienia obiektu XPCNativeWrapper

Jawne obiekty XPCNativeWrapper istnieją dopóki, dopóty istnieją do nich odwołania. Utworzenie nowego jawnego obiektu XPCNativeWrapper dla tego samego potencjalnie niezabezpieczonego obiektu object spowoduje utworzenie nowego obiektu opakowującego; należy mieć to na uwadze przy ustawianiu własności „expando”.

Niejawne obiekty XPCNativeWrapper istnieją tak długo, jak opakowywane przez nie obiekty.

Ustawianie własności „expando” obiektu XPCNativeWrapper

Możliwe jest ustawienie własności „expando” (tj. własności o nazwach, które nie mają odpowiedników wśród własności zdefiniowanych w specyfikacji IDL) dla obiektów XPCNativeWrapper. Własności te będą widoczne z poziomu chrome, ale nie będzie można uzyskać do nich dostępu z poziomu zawartości (content). Nie istnieje bezpieczny sposób ustawienia własności „expando” z poziomu chrome i odczytania jej następnie z poziomu zawartości.

Niezabezpieczony dostęp do własności

Jeżeli z jakiegoś powodu wymagany jest niezabezpieczony dostęp do własności, można go uzyskać za pomocą własności wrappedJSObject obiektu opakowującego. Jeżeli na przykład obiekt docWrapper jest obiektem opakowującym obiekt doc, to własność

docWrapper.wrappedJSObject.prop

jest identyczna z własnością

doc.prop

Jak podkreślono w tytule niniejszej sekcji, technika ta nie jest bezpieczna. Własności wrappedJSObject nie należy używać w kodzie produkcyjnym w celu ominięcia pośrednictwa obiektu XPCNativeWrapper.

W programie Firefox 3 własność wrappedJSObject zwraca kolejny obiekt opakowujący obiektu zawartości JavaScript (XPCSafeJSObjectWrapper), co pozwala na bezpieczne sprawdzenie obiektu zawartości — patrz Obiekty opakowujące XPConnect#XPCSafeJSObjectWrapper.

Informacje o lepszych rozwiązaniach alternatywnych znajdują się w artykule Interakcja pomiędzy stronami uprzywilejowanymi i stronami bez przywilejów.

Znane błędy

W wersjach z serii 1.5.0.x występują dwa znane błędy w obsłudze obiektów XPCNativeWrapper:

  1. W wersjach Firefoksa od 1.5 do 1.5.0.4 występuje błąd 337095, w wyniku którego w niektórych przypadkach dla skryptów chronionych nie są tworzone obiekty opakowujące. Z reguły, gdy skrypt chroniony uzyskuje dostęp do własności lub wywołuje funkcję, a ta własność lub funkcja zwraca obiekt niezaufany, tworzony jest obiekt opakowujący. Jeżeli jednak funkcja w skrypcie chronionym jest wywoływana z poziomu kodu w języku C++, a jako argument jest do niej przekazywany obiekt niezaufany, obiekt opakowujący nie zostanie utworzony. W funkcjach, które mają być wywołane w taki sposób, należy zaimplementować własną technikę opakowania. Błąd ten naprawiono w Firefoksie w wersji 1.5.0.5 i nowszych.
  2. W wersjach Firefoksa od 1.5 do 1.5.0.5 występuje błąd 345991, w wyniku którego komponenty utworzone przy użyciu języka JavaScript nie są skryptami chronionymi. Błąd ten naprawiono w Firefoksie w wersji 1.5.0.6 i nowszych.

Ograniczenia w korzystaniu z obiektu XPCNativeWrapper

Niektóre często stosowane własności i style kodowania nie mogą być używane z obiektami XPCNativeWrapper:

  1. Przypisywanie wartości lub odczytywanie własności on* obiektu XPCNativeWrapper opakowującego węzeł DOM lub obiekt window spowoduje zgłoszenie wyjątku. (Zamiast tej techniki należy użyć metody addEventListener oraz — jeżeli używana była dyrektywa return false; — należy zastąpić ją metodą event.preventDefault() w kodzie obserwatora).
  2. Nie jest możliwy dostęp do ramek określanych przy użyciu nazwy okna (np. window.nazwaRamki) w odniesieniu do obiektów XPCNativeWrapper.
  3. Nie jest możliwy dostęp do własności document.all w odniesieniu do obiektu XPCNativeWrapper opakowującego obiekt document.
  4. Nie jest możliwy dostęp do nazwanych elementów określanych przy użyciu nazw w odniesieniu do obiektu XPCNativeWrapper opakowującego dokument HTML. Jeżeli na przykład istnieje element <form name="foo">, a obiekt docWrapper jest obiektem opakowującym dokument HTML doc, to własność doc.foo zwraca obiekt HTMLFormElement, ale własność docWrapper.foo zwraca wartość undefined. Aby uzyskać dostęp do nazwanych elementów, należy użyć metody docWrapper.forms.namedItem("foo").
  5. Nie jest możliwy dostęp do elementów określanych przy użyciu identyfikatorów w odniesieniu do obiektu XPCNativeWrapper opakowującego dokument HTML. Należy użyć metody getElementById.
  6. Nie jest możliwy dostęp do elementów input określanych przy użyciu nazw w odniesieniu do obiektu XPCNativeWrapper opakowującego formularz HTML. Należy użyć metody form.elements.namedItem("nazwaElementuInput").
  7. Nie jest możliwy dostęp do elementów określanych przy użyciu nazw w odniesieniu do obiektu XPCNativeWrapper opakowującego obiekt HTMLCollection. Należy użyć metody namedItem(). Uwaga: metoda namedItem zwraca jedynie pierwszy element input o danej nazwie, nawet jeżeli w formularzu znajduje się więcej elementów o tej samej nazwie (np. przycisków radiowych).
  8. Nie jest możliwe wywoływanie metod zaimplementowanych przez wtyczki NPAPI za pośrednictwem obiektu XPCNativeWrapper opakowującego odpowiadający im węzeł.
  9. Nie jest możliwe ustawianie i pobieranie własności zaimplementowanych przez wtyczki NPAPI za pośrednictwem obiektu XPCNativeWrapper opakowującego odpowiadający im węzeł.
  10. Nie jest możliwe wywoływanie metod zaimplementowanych poprzez wiązania XBL dołączone do węzła za pośrednictwem obiektu XPCNativeWrapper opakowującego ten węzeł.
  11. Nie jest możliwe ustawianie ani pobieranie własności zaimplementowanych poprzez wiązania XBL dołączone do węzła za pośrednictwem obiektu XPCNativeWrapper opakowującego ten węzeł.
  12. Przy wyliczaniu własności obiektu XPCNativeWrapper za pomocą pętli "for (var p in obiekt_opakowujący)" nie są wyliczane własności zdefiniowane w specyfikacji IDL.
  13. Własność Object.prototype nie jest częścią hierarchii prototypów obiektu XPCNativeWrapper. W rezultacie wiele własności obiektu Object.prototype pozostaje niezdefiniowanych w odniesieniu do obiektu XPCNativeWrapper (są to własności __proto__, __parent__, __count__, toSource, toLocaleString, valueOf, watch, unwatch, hasOwnProperty, isPrototypeOf, propertyIsEnumerable, __defineGetter__, __defineSetter__, __lookupGetter__ oraz __lookupSetter__).
  14. Nie jest obsługiwana metoda importXPCNative dostępna w starszych implementacjach obiektów XPCNativeWrapper.
  15. Nie jest możliwy dostęp do klas standardowych (takich jak Function) za pośrednictwem obiektu XPCNativeWrapper. Aby utworzyć funkcje i obiekty powiązane z rodzicem danego okna, należy skorzystać z metody eval tego okna.

Artykuł Avoid Common Pitfalls in Greasemonkey (Jak uniknąć typowych pułapek w Greasemonkey; tekst w jęz. ang.) zawiera obszerne omówienia niektórych z powyższych ograniczeń (w odniesieniu do skryptów Greasemonkey).


Autorzy i etykiety dokumentu

Autorzy tej strony: Flaneur, Mgjbot
Ostatnia aktualizacja: Flaneur,