Hámorszky Balázs
Diplomaterv-téma bejelentő
1
Hámorszky Balázs
1 Nyilatkozat Alulírott Hámorszky Balázs, a Pázmány Péter Katolikus Egyetem Információs Technológiai Karának hallgatója kijelentem, hogy ezt a szakdolgozatot meg nem engedett segítség nélkül, saját magam készítettem, és a szakdolgozatban csak a megadott forrásokat használtam fel. Minden olyan részt, melyet szó szerint, vagy azonos értelemben, de átfogalmazva más forrásból átvettem, egyértelműen a forrás megadásával megjelöltem. Ezt a Szakdolgozatot más szakon még nem nyújtottam be.
2012-05-15
_____________________________ Hámorszky Balázs
2
Hámorszky Balázs
2 Tartalomjegyzék Diplomaterv-téma bejelentő......................................................................................................1 1 Nyilatkozat..............................................................................................................................2 2 Tartalomjegyzék......................................................................................................................3 3 Absztrakt.................................................................................................................................6 4 Abstract...................................................................................................................................7 5 Bevezető.................................................................................................................................8 6 A téma áttekintése.................................................................................................................10 6.1 Szoftver fejlődésének és felhasználói számának kapcsolata..........................................10 6.2 Megszokás faktor..........................................................................................................10 6.3 A Windows felhasználói felülete...................................................................................11 6.4 XULRunner bemutatása................................................................................................12 6.5 A prototípus projekt.......................................................................................................13 6.6 Összehasonlítás.............................................................................................................14 7 Elméleti háttér.......................................................................................................................15 7.1 A JavaScript programozási nyelv..................................................................................15 7.2 Osztály alapú és prototípus alapú nyelvek....................................................................16 7.3 Konstruktor...................................................................................................................16 7.4 Öröklődés......................................................................................................................17 7.5 Tulajdonságok...............................................................................................................17 7.5.1 Publikus és privát tagok........................................................................................18 7.5.2 Hibakezelés...........................................................................................................18 7.5.3 Identifikációs függvény.........................................................................................19 7.6 JavaScript patternek......................................................................................................19 7.6.1 Kontextus..............................................................................................................19 7.6.1.1 Metódushívás................................................................................................20 7.6.1.2 Függvényhívás..............................................................................................20 7.6.1.3 Konstruktor...................................................................................................21 7.6.1.4 Alkalmazott meghívás...................................................................................21 7.6.2 Closure..................................................................................................................21 7.6.3 Lusta kiértékelés...................................................................................................22 7.6.4 Module pattern......................................................................................................23 7.7 Mozilla Gecko JavaScript kiterjesztések.......................................................................24 3
Hámorszky Balázs 7.7.1 Modulok................................................................................................................24 7.7.2 let..........................................................................................................................24 7.7.3 const......................................................................................................................24 7.7.4 XPCOM................................................................................................................25 7.8 Kurrens JavaScript technológiák...................................................................................25 7.8.1 NodeJS..................................................................................................................25 7.8.2 Emscripten............................................................................................................26 7.8.3 jslinux...................................................................................................................26 7.8.4 CoffeScript............................................................................................................26 7.8.5 Coco......................................................................................................................26 7.8.6 Asztali alkalmazások.............................................................................................27 7.8.7 Futtatókörnyezet...................................................................................................27 7.8.7.1 JIT.................................................................................................................28 7.8.7.2 Garbage Collector.........................................................................................28 8 Megvalósítás.........................................................................................................................30 8.1 XULRunner..................................................................................................................30 8.2 Command......................................................................................................................30 8.3 Tree...............................................................................................................................31 8.4 nsITreeView..................................................................................................................31 8.5 CSS...............................................................................................................................33 8.6 Chrome URL................................................................................................................34 8.7 Ikonok...........................................................................................................................35 8.8 Atomok.........................................................................................................................37 8.9 Kétpaneles elrendezés...................................................................................................38 8.10 Lokalizálás..................................................................................................................38 8.11 Navigálás a fájlrendszerben.........................................................................................39 8.12 Rendezés.....................................................................................................................39 8.13 Main Event Loop........................................................................................................41 8.14 Szálkezelés..................................................................................................................42 8.15 Aszinkron másolás......................................................................................................42 8.15.1 Másolási ablak....................................................................................................42 8.15.2 ChromeWorker....................................................................................................43 8.15.3 Méret megállapítása............................................................................................44 8.15.4 nsIAsyncStreamCopier.......................................................................................44 4
Hámorszky Balázs 9 Tervek a jövőre.....................................................................................................................46 10 Összefoglaló.......................................................................................................................47 Ábrajegyzék............................................................................................................................48 Irodalomjegyzék.....................................................................................................................49
5
Hámorszky Balázs
3 Absztrakt A Linuxot lazán összekapcsolt modulok alkotják, így biztosítva lehetőséget a felhasználók számára többféle felületből való választásra. Ezen diverzitás ellenére a Linuxnak máig sem sikerült kiterjedt felhasználói bázisra szert tennie. A dolgozat alap felvetése, hogy egy a Windowséhoz egyrészt kinézetben, másrészt felépítésében is hasonló felülettel a felhasználók átállását jelentősen meg lehetne könnyíteni. Mivel a Windows felülete nagy részben össze van fonódva a benne található alapértelmezett Internet Explorer böngészővel, ezért kézenfekvő választás Linuxon a Mozilla Firefox alapját adó XULRunner környezetre felépíteni egy új asztalkörnyezetet. Egy asztalkörnyezet felépítésének komplexitásosából fakadóan több ember több éves munkáját igényelné, ezért a dolgozat ennek egy komponensének megvalósíthatóságának bizonyítására kíván bemutatni egy prototípus projektet annak elméleti hátterével együtt. A dolgozat tehát bemutatja: •
a JavaScriptet, mint nyelvet és annak speciális képességeit, más elterjedt nyelvekkel összehasonlítva
•
a JavaScript Mozilla által nyújtott kiterjesztéseit
•
egy XULRunner alapú szoftver standard felépítését
•
a XUL felületek felépítésére és programozására nyújtott lehetőségeit
•
a XULRunner által biztosított lehetőségeket fájlműveletek elvégzésére
Ezek a témakörök önmagukban egyesével is elég nagyok ahhoz, hogy egy külön dolgozat témái lehessenek, ezért csak a dolgozat témájába vágó részeik kerülnek bemutatásra. A dolgozat nem csak elméleti síkon kívánja körüljárni a prototípus projekt elkészítését, hanem eredménykép egy működő fájlkezelő alkalmazást is szeretne létrehozni.
6
Hámorszky Balázs
4 Abstract Linux consists of loosely coupled modules, and provides the user the choice of different user interfaces. Despite of this diversity, Linux cannot get hold of a wide user base. The initial assumption of the thesis is that an interface similar to Windows in its look and structure can make the transition process for the user much easier. As the Windows user interface is tightly coupled with the integrated Internet Explorer browser, an obvious choice is to do the same on Linux by using the XULRunner runtime that gives the base of Mozilla Firefox. Building a complete desktop environment would require the work of a group of programmers for years, so the thesis provides a proof of the feasibility a base component through a prototype project along with its theoretical background. The thesis describes: •
The JavaScript language with its unique abilities, in comparison with other popular languages
•
The JavaScript extensions by Mozilla
•
The standard structure of a XULRunner project
•
The tools XUL provides to build user interfaces
•
The file operations provided by XULRunner
Each of these topics is complex enough to be the base of a whole thesis, so only those subsets will be presented, which are in connection with the topics of this thesis. The thesis not only tries to show the theoretical background of the prototype project, but at the same time it also tries to produce a whole working file manager application.
7
Hámorszky Balázs
5 Bevezető Úgy érzem, hogy a szabad szoftver közösségtől rengeteget kaptam, sokkal lakályosabbá tették számomra a számítógépes világot, mintha a zárt forráskódú szoftvereknél ragadt volna le a világ. Nem tartom magam járatlannak a Windows világban sem, tisztában vagyok vele, hogy az átlag felhasználók végül is miért nem váltanak Linuxra. Egy ecosystem fejlődése egyenes arányban áll a felhasználói számával. Ha teljes mértékben önző módon azt kívánom, hogy a Linux gyorsabban fejlődjön, akkor ez egyet jelent azzal a kívánsággal, hogy több legyen a felhasználója. Úgy lehet több felhasználót csábítani a Linux környékére, ha nem a hackerek 1, geekek2 és nerdök3 által kidolgozott felület fogadja őket (ami egyébként feltehetőleg nagyon hatékony), hanem valami ismerős, valami olyan, ami otthonos, kényelmes és megszokott. Úgy gondolom, hogy ez úgy a legkönnyebben elérhető, ha nem csak kinézetben hasonló ez a felület, hanem koncepcionálisan is. Így a fejlesztési idő is részben csökken, ami szükséges egy ilyen felület elkészítéséhez. A Windows felülete többé-kevésbé az Internet Explorer motorjával van felépítve. Nem tudom, hogy az Internet Explorer milyen kényelmi lehetőségeket kínál egy felület elkészítésében, de a piacon (a Windows monopóliumának okán) a második legnépszerűbb böngésző a Mozilla által készített Firefox rengeteggel tud szolgálni. Emellett a Firefox alapértelmezetten fut Linuxon, ameddig az Internet Explorer csak igen nehézkesen indítható el. A célkitűzésem így hát nem más, mint egy teljes asztalkörnyezet elkészítése Linux rendszerekre a Mozilla által fejlesztett XUL technológia és Gecko[1] futtatómotor használatával. Nem éreztem megvalósíthatónak, hogy azonnal egy nekifutásra kivitelezzek egy ilyen (bevallhatóan, egy ember számára meglehetősen) grandiózus tervet. Az asztalkörnyezetek felépítésének elemzése során kézenfekvően adódik, hogy hogyan érdemes a megvalósításhoz hozzáfogni. A legközpontibb, leglényegesebb elem a felhasználók számára, hogy 1
„Hacker (programmer subculture), who shares an anti-authoritarian approach to software development now associated with the free software movement” (Wikipedia)
2
„The word geek is a slang term, with different meanings ranging from "a computer expert or enthusiast" to "a carnival performer who performs sensationally morbid or disgusting acts", with a general pejorative meaning of "a peculiar or otherwise dislikable person, especially one who is perceived to be overly intellectual".” (Wikipedia)
3
„Nerd is a term that refers to an intelligent but single-minded person obsessed with a nonsocial hobby or pursuit.” (Wikipedia)
8
Hámorszky Balázs hozzáférhessenek valamilyen konzisztens módon a fájljaikhoz, tehát egy fájlkezelő kidolgozását tűztem ki elsődleges célnak. Ilyen szempontból visszanyúltam a gyökerekig, és egy a Norton Commander és társai által bevezetett, és később a jelenleg is nagy népszerűségnek örvendő Total Commander révén folytatott, két paneles fájlkezelők hagyományába illeszkedő prototípus-alkalmazás elkészítése mellett döntöttem.
9
Hámorszky Balázs
6 A téma áttekintése 6.1 Szoftver
fejlődésének
és
felhasználói
számának
kapcsolata Minden szoftverre igaz, hogy a felhasználóinak száma közvetlenül befolyásolja a fejlődését. Ha egy fizetős szoftvert sokan használnak, akkor a készítő cégnek sok pénze lesz és ebből még többet tud a fejlesztésre fordítani. A gyorsan fejlődő szoftverek jobban alkalmazkodnak a változó feltételekhez, körülményekhez és igényeknek, ezért többen lesznek velük elégedettek és még több pénzt tud a több felhasználó befizetéseiből a fejlesztő cég a fejlesztésre költeni. Ez a „végtelen” spirál a nyílt forrású, ingyenes programok esetében is hasonlóan néz ki, kis eltéréssel. Ugyan igaz, hogy vannak fejlesztő cégek, akik a terméktámogatásokat szokták értékesíteni a nyílt forrású termékekhez és ugyan úgy nagy részt vállalnak a fejlesztésben, de itt egy másik faktor is fontos szerepet játszik. A felhasználók általában nem a pénzükkel, hanem a hibák bejelentésével támogatják a fejlesztést, így nagyobb megbízhatóságot biztosítva a szoftver számára. Az átlag felhasználók létszámával arányosan emelkedik a hozzáértő felhasználók és a fejlesztők száma is, valamint egy olyan csoporté is, akiket talán fejlesztő felhasználóknak nevezhetnénk. A fejlesztő felhasználó fontos ismérve, hogy nem rendszeres fejlesztője az adott programnak, de meg lennének a képességei, hogy az legyen. Az ilyen felhasználók, mikor a program használata közben hibába vagy hiányosságba ütköznek, vagy csak támad egy jó ötletük, amivel ki tudnák egészíteni azt, így javítva a felhasználói élményt, sokszor meg is teszik ezt. Ők számszerűen ugyan kevesebben vannak, mint az átlagos felhasználók, de a számuk teljesen arányosan növekszik együtt az átlag felhasználóval. Elő fordulhat az is, hogy a sokadik ilyen fejlesztés után elég motivációt érez magában az ilyen felhasználó fejlesztő, hogy elkezdje magáé ként elfogadni az adott programot és rendszeres fejlesztőként csatlakozni a fejlesztőkhöz.
6.2 Megszokás faktor Ezek után levonhatjuk a következtetést, hogy a Linux felhasználók számával együtt nő a teljes Linux rendszer használhatósága, bővül funkcionalitásában, stabilitásában, a megbízhatóság minden aspektusában és gyorsul a fejlődése is. Ugyanakkor van egy másik nagyon fontos faktor is a felhasználók egy adott rendszer mellé pártolásában. Egy olyan fiatal gyereket, aki még nem használt számítógépet megtanítani egy adott rendszer használatára nagyságrendekkel 10
Hámorszky Balázs könnyebb, mint egy olyan embert, aki már sok tapasztalatot szerzett egy másik rendszer használatában, azt rutinosan használja. Tehát érdemes foglalkozni a megszokás faktorral. Ha például az ember egy új autót vesz, az elején még nagyon szokatlan számára, sokszor talán meg is bánja a döntését, de mivel már kifizetett rá egy nagyobb összeget, ezért nem fog tőle csak úgy megválni, ezért szép lassan hozzá szokik. Ez a szoftverek tekintetében persze teljesen más. A szoftver vagy ingyen van, vagy egy autóhoz képest nevetséges összegekbe kerül, ezért ha az átszokás nehezebben megy annál, mint amennyit az ember indokoltnak érez azért, hogy elérje azokat az előnyöket, amiket az újonnan választott szoftver feltételezései szerint nyújtani tud számára, könnyedén megválik tőle és visszatér a kaptafához. Mi történik akkor, ha az új szoftver ugyan arra a kaptafára megy, mint a régi, csak többet nyújt? Természetesen ebben az esetben a döntés egyértelmű az egyszeri felhasználó számára. Ez tipikusan a verzióváltás esete lehet, de egy másik esetben is lehetséges. Ha a másik „termék” elég pontosan veszi magára a vetélytárs mimikrijét, a felhasználó számára többet nyújtva, akár csak a stabilitás terén, máris egyértelmű választást adhat. Ezen a ponton tehát látható, hogy mondjuk számomra, mint Linux felhasználó számára kívánatos lehet, hogy a Windows felhasználók meglehetősen jelentős táborából áttérjenek felhasználók Linuxra, mert ezzel az általam használt rendszer javulásához, jobbulásához járulnak hozzá több területen is. Ezért egy a Windows-éra eléggé hasonlító felület létrehozása Linuxra több mint kívánatos lehet.
6.3 A Windows felhasználói felülete A 98-as Windows bevezetésétől az Internet Explorer 7 előttig végig a felhasználói felület és a böngésző ugyan az a program volt és ugyan azokat a komponenseket használta. Ugyan az Internet Explorer 7-től kezdve ezt a lehetőséget megszüntették, de ez mégis egy érdekes ötletnek tűnt számomra. A Microsoft böngésző legnagyobb riválisa a Mozilla Firefox, ami koránt
sem
csak
Weblapok
megjelenítésében
jeleskedik,
de
felhasználói
felület
megjelenítésében is sokkal fejlettebb lehetőségeket kínál, mint vetélytársai. A megoldás neve XUL (XML User Interface Language) ami egy nagyon szerteágazó, dinamikus lehetőségekkel rendelkező technológia. XUL-ben egy átlagos felhasználói felület leírása egyszerűségben vetekszik a HTML-el. Magát a felület működését pedig ahhoz hasonlóan JavaScriptben lehet kódolni. Bár ez a technológia népszerű, de talán koránt sem annyira, mint amennyire megérdemelné. Sajnos a JavaScript-re sokan úgy tekintenek, mintha csak Weboldalak mozgó elemeit lehetne vele kódolni vagy 11
Hámorszky Balázs éppen AJAX kéréseket futtatni. Esetleg arra, hogy a reklámozók a Weben még idegesítőbb reklámokat csinálhassanak. Aki így gondolja, nem is tévedhetne nagyobbat. Az alap ötletem tehát az volt, hogy ezzel a megoldással valósítsam meg a teljes asztal környezetét egy Linux rendszernek, ami persze a XUL-ben rejlő lehetőségek miatt nem feltétlenül csak a Windows-éra hasonlíthatna, de nagyon jól testre is szabható lenne a Mozilla Firefox-hoz hasonlóan.
6.4 XULRunner bemutatása A XULRunner az XUL alapú alkalmazások futtatására szolgáló runtime („futtató környezet”). A XULRunner C/C++ nyelven íródott és az elsődleges feladata XML-ben definiált felületek megjelenítése, mint a XUL és a HTML. Az XML-ek feldolgozását és megjelenítését végző komponense a Gecko megjelenítési réteg. A Gecko működése nagyon vázlatosan így képzelhető el
1. ábra: Gecko működése
12
Hámorszky Balázs A Gecko a következő technológiákat biztosítja (a teljesség igénye nélkül)[2]: •
XPCOM A Microsoft ActiveX technológiájához hasonló Component Object Model.
•
XBL XML Binding Language. Ez az XML alapú nyelv elemek összekapcsolására szolgál. Ennek segítségével lehet más viselkedéssel felruházni elemeket a dokumentumban.
•
SVG Ez az XML formátum vektorgrafikus képek leírására szolgál.
•
XSLT Extensible Stylesheet Language Transformations. Ez szolgál az XML állományok transzformálására.
•
XML
Extrák
Ide tartozik az XMLHttpRequest, amivel egy HTTP kérésen lehet XML formátumban adatokat cserélni egy Web szerverre, illetve a DOMParser, amivel XML fájlokat lehet futás időben feldolgozni. Az XUL-ben leírt felület elemeinek definiálása a hasonló megoldásokkal összehasonlítva is nagyon könnyű. Egy gomb definiálása a felületen például:
Egy egész eszközsáv létrehozható csak ennyivel:
Hasonló lehetőségek persze más megoldásokban is elérhetők, de a XUL-ben készült alkalmazás esetén a felület viselkedésének definiálása nagyságrendekkel egyszerűbb, mint más esetekben, mivel ezt a Web programozók által is jól ismert JavaScript nyelven lehet megtenni, nem pedig C, C++ vagy hasonló nyelven. Bár az ilyen nyelvekben is megvalósítható a DOM modell kezelése, de a JavaScript számára, ez anyanyelvnek mondható. Rengeteg lehetőséggel támogatja a Gecko motor a programozót, hogy gördülékenyebb legyen az alkalmazás viselkedésének programozása.
6.5 A prototípus projekt Mielőtt bármilyen kódolásba belefogtam volna egy hosszas kutatási periódussal kezdtem. Az ilyen rendszerek egyik alap komponense, amit ablak kezelőnek hívnak, amiből már most is annyi -féle érhető el, hogy bárki megtalálhatja a maga számítását és biztos, hogy ráakad egy olyanra, ami minden igényét kielégíti. Egy új ablakkezelő elkészítése tehát meg sem fordult a 13
Hámorszky Balázs fejemben. A mostani asztal-környezet rendszerekben is általában cserélhető az ablakkezelő, ezért ezt a megközelítést én is szándékozom átvenni.
6.6 Összehasonlítás Felvetődhet a kérdés, hogy mi értelme van egy teljesen új asztal környezet írásának Linuxra. A kérdés jogos, az ilyen megoldásokból már most is Dunát lehet rekeszteni. Talán az egyik legismertebb a GNOME, ami például az Ubuntu disztribúciónak is az alap komponense és ez is magas fokon testre szabható. Hasonlóan népszerű a KDE is, ami látványban még a MacOSel is felveszi a versenyt, vagy például az Xfce, ami ezeknél sokkal kisebb rendszerigényével hívja fel magára a felhasználók figyelmét. Amiben felül múlhatná őket egy XUL alapú megoldás, az a felület dinamikája. Dinamika az új elemek felépítésének gyorsasága szempontjából, és dinamika olyan szempontból, hogy a telepített rendszer a Mozilla Firefoxhoz hasonlóan kiegészítőkkel olyan mértékben lenne testre szabható, amilyen mértékben a többi megoldás ezt nem teszi lehetővé. Fontos újdonság lehetne a Webes szolgáltatások kihasználása a felület legalacsonyabb szintjeitől. Egyik technológia sem támogatja olyan mértékben a Webes szolgáltatások elérését, mint a XULRunner. Ez főleg abból vezethető le, hogy elsődleges funkciója Webböngésző volt. A Mozilla Firefox kiegészítők már most is Webes szolgáltatások egész tárházát teszik elérhetővé egyetlen kattintással. Az a tendencia látszik, hogy a felhasználói programok nagy része már nem a személyi számítógépekre van telepítve, hanem csak Webböngészőn keresztül éri el a felhasználó, ezért az offline alkalmazások használatával töltött idő egyre csökken, a Weben használható alkalmazások és „egyszerű Weboldalak” között pedig a határvonal elmosódni látszik. A Cloud Computing megjelenésével és rohamos terjedésével pedig csak egyre erősödik ez a folyamat. Ebbe a tendenciába illeszkedik bele egy alapvetően Web böngészésére tervezett platform használata akár az egyik legalapvetőbb felhasználói program kiszolgálására. Ha ez ilyen jó és kézenfekvő ötletnek tűnik, miért nem jutott eszébe eddig senkinek? Erre a kérdésre csak felemás választ lehet adni. A válasz igen is és nem is. Igen, mert már próbálkozott más is olyan asztalkörnyezettel, ami Mozilla Firefoxra épül. A Pyro Desktop nevű kezdeményezés nem egyedülálló alkalmazásként volt elérhető, hanem csak Mozilla Firefox kiegészítőként. Elsődlegesen nem egy teljes asztalkörnyezet volta, hanem ablakkezelő. A meglehetősen tüzes neve ellenére hamar hamvába holt. Mára már elérhetetlen a honlapja és az internetes archívumok tanúsága szerint 2007 júliusában változtattak rajta utoljára bármit[3].
14
Hámorszky Balázs
7 Elméleti háttér Mivel az XUL alkalmazások viselkedése JavaScript-ben programozható, ezért első lépésként ezzel a nyelvel kellett megismerkednem[4]. Bár már rendelkedztem valamennyi tapasztalattal, hiszen az alapvető Webes alkamazások is ezene a nyelven programozhatók és volt már dolgom ilyenekkel, de rengeteg érdekes tulajdonságot sikerült felderítenem a nyelvvel kapcsolatban.
7.1 A JavaScript programozási nyelv A JavaScript a Java-val szemben nem szigorúan típusos nyelv. Ezt lehet hátránynak és előnynek is tekinteni. Az olyan nyelveknél, mint a Haskell, amiknek a típus rendszere még a Java-énál is fejlettebb, a függvények típusának megadását a matematikával párhuzamot vonva úgy lehet tekinteni, mint amikor egy matematikus kimond egy tételt. Az pedig, mikor a függvényt definiáljuk, azzal ekvivalens lépés, mint mikor a tételt bizonyítják. Az ilyen erősen típusos nyelveknél a programozás módszertani bizonyítás gyakorlatilag erőfeszítés-mentes, mert a leírt programkód bizonyítja önmagát. Az olyan nyelvekről, mint a Java, ezt már nem lehet elmondani, de még azoknál is könnyű a bizonyítás. A gyengén típusos nyelveknél viszont ember legyen a talpán, aki nekiáll bizonyítani egy kódot. Mondhatni ez a sötét oldala a JavaScripthez hasonló nyelveknek. A gyengén típusosságnak ugyanakkor vannak előnyei. Ha körültekintően használja a programozó, sokkal rövidebb megoldásokat tud adni az egyes problémákra, így átláthatóbb és karbantartható kódot kaphat. Itt egy paradox helyzet áll elő. A gyengébb programozók inkább gyengén típusos nyelvekben tanulnak meg programozni, ami káros, mert mindenféle nemvárt viselkedést produkálhat nekik a nyelv és a fordítója vagy futtató környezete. A legerősebben típusozott nyelveket pedig már jóformán csak képzett programozók szokták használni, mert egy kezdő programozó számára nehézséget jelent olyan kód előállítása, amit elfogad a fordító vagy értelmező. Ezzel szemben az erősebb típusrendszernek pont az lenne az egyik előnye, hogy a programozónak nem kell annyira körültekintőnek lennie, mert a típusrendszer sok hibától megvédi. Persze ezt a kérdést egyáltalán nem csak ebből a szempontból lehet nézni. Én nem is vizsgálnám most ezt nagyobb mélységekben és nem szeretnék a nézetek között igazságot tenni, de tény, hogy a JavaScript típusrendszere sokkal megengedőbb, mint például a (nem véletlenül) hasonlóan elnevezett Java-é, ezért minél bonyolultabb osztálystruktúrát készít benne az ember, annál körültekintőbbnek kell lennie.
15
Hámorszky Balázs
7.2 Osztály alapú és prototípus alapú nyelvek Az osztály alapú nyelvek, mint a Java és a C++, alapvetően két komponensből állnak. Osztályokból és a belőlük létrehozott példányokból. •
Osztály Az osztály egy absztrakt szerkezet. Nem egy adott „egyedet” ír le, hanem az egy csoportba tartozó egyedek tulajdonságait és a hozzájuk tartozó metódusokat.
•
Példány A példány ezzel ellentétben egy osztály példányosítása, tehát az osztály által leírt csoporthoz tartozó egyetlen egyed. Csak pontosan azokkal a tulajdonságokkal rendelkezik, mint az osztály, amiből példányosítva lett, se több, sem kevesebb tulajdonsága nem lehet.
A prototípus alapú nyelveknél némiképp eltér a hozzáállás. Nem különül el az osztály és a példány egymástól. Ha az osztály alapú objektum orientált nyelvekhez szeretnénk hasonlítani a prototípus alapúakat, akkor úgy tekinthető, mintha minden csak példány lehetne. Az eltérés abban van, hogy a példányok lehetnek ősosztályok egy új példány számára, tehát lehetnek azok prototípusai.
7.3 Konstruktor Osztály alapú nyelveknél az osztály fordítás időben definiálódik. Ekkor definiálódnak a benne tárolt adattagok, a metódusai és a konstruktor függvénye. Ez utóbbi az osztály egy metódusa, ami kezdő értéket tud adni az osztály adattagjainak és el tud végezni bármilyen olyan feladatot, amit a példány létrehozásakor kell elvégezni. Ezek után futás időben jönnek létre példányok az osztályból. A létrehozási folyamat fontos része a konstruktor függvény, de az üres is lehet. JavaScriptben minden futásidőben történik. Maga az osztály nem kerül definiálásra a Java-ban megszokott módon. Az objektum kezdeti formája egyetlen függvényben van leírva, amit itt is konstruktor függvénynek hívunk. Különlegesség, hogy bármilyen JavaScript függvény használható konstruktor függvényként, bár pár speciális elvárásnak meg kell felelnie, hogy értelmesen használható legyen utána objektumként a vele létrehozott példány. A létrehozás a Java-ban megszokott módon a new kulcsszóval történik.
var objom = new Objektumom;
16
Hámorszky Balázs
7.4 Öröklődés Öröklődésnél az osztály alapú nyelveknél az öröklő osztály átveszi az ősosztály minden tulajdonságát. Erre JavaScriptben is van lehetőség. Mivel nincsenek osztályok, ezért nem lehet osztályhierarchiát sem felépíteni. Nem meglepő módon itt csak prototípus hierarchia létrehozására van lehetőség. Az öröklő objektum a prototípus tulajdonságán keresztül kapja meg az ős konstruktorát. A következő példában látható módon:
function os(){ this.tul1 = „barmi”; } function al(){ this.tul2 = „akarmi”; } al.prototype = new os; 7.5 Tulajdonságok Tulajdonságok vagy másképp mondva adattagok, illetve megint másképp belső adattagok esetében két félét különböztetünk meg. Egyik a sima saját tulajdonság, a másik a prototípikus tulajdonság. A sima tulajdonságokhoz képest a prototípikus tulajdonságokat az előző példához hasonlóan a .prototype bevezetésével lehet hozzáadni az objektumhoz.
os.prototype.tul3 = „mas”; os.tul4 = „egyeb”; Két szempontból van különbség a kettő között. Egyik az öröklődés. Ha futásidőben új prototípus tulajdonságot adunk hozzá egy ős objektumhoz, akkor automatikusan örökli az összes leszármazottja, ameddig a sima tulajdonságot csak a hozzáadás után létrehozott leszármazottjai öröklik. Az objektum metódusainak esetében a példányok egy teljes másolatot kapnak a metódusból. Mivel ezek módosíthatók menet közben, így az egyes példányok metódusai nem módosulnak automatikusan. A prototípus metódusként definiált metódusok ugyanakkor nem másolódnak le, így nem foglalnak minden példánynál külön memóriát. Ha tudjuk, hogy egy adott metódust nem akarunk futásidőben felül definiálni, akkor érdemesebb prototípus metódusként definiálni, mert ebben az esetben jelentős mennyiségű memóriát takaríthatunk meg. 17
Hámorszky Balázs
7.5.1 Publikus és privát tagok Akár, csak Java-ban, lehetőség van publikus és privát adattagok definiálására is. Privát adattagokat
a
var
kulcsszóval
bevezetve
lehet
hozzáadni
az
objektumhoz.
A
konstruktorfüggvényen belül definiált függvények alapértelmezetten privát függvények.
function os(){ var ptul = „sajat”; funcion pfv(){ return „ezt”; } } Publikus adattagokat a this. bevezetéssel lehet hozzáadni. A publikus függvényeket nevesíteni kell a külvilág felé.
function os(){ this.putul = „lathato”; this.pufv = funcion (){ return „azt”; } } A this. bevezetéssel megadott változókat és függvényeket később az elérésükhöz is a this. bevezetéssel kell hivatkozni.
7.5.2 Hibakezelés Fontos része egy nyelvnek a hibák kezelése. A JavaScript hibák kezelésére egy nagyon jól ismert megoldást nyújt, a try-catch blokk formájában.
try { ... } catch (ex) { alert(ex) 18
Hámorszky Balázs
} 7.5.3 Identifikációs függvény Nagyon fontos, hogy nem identitás függvényről van szó. Identifikáció alatt azt értem, amikor egy adott id-vel ellátott DOM objektumot kikeres a JavaScript és utána műveleteket hajt végre rajta.
element = document.getElementById(id); Erről a Mozilla wikiben is van dokumentáció. Sok publikusan hozzáférhető kódban láttam leírva a $('id') kifejezést. Erről semmilyen dokumentációt nem találtam a Mozilla wikiben. A $ függvényre valók keresgélésben az volt a nehézség, hogy a legtöbb keresőmotort lehetetlen rávenni, hogy tényleg keressen is rá a $ jelre (legalábbis én nem jártam sikerrel az ez irányú próbálkozásaim közben). Hosszas kutakodás után annyit sikerült kiderítenem, hogy a $ sok más nyelvvel szemben (mint például a Perl), semmilyen extra jelentéstartalommal sem rendelkezik és pontosan ugyan olyan névben használható karakter, mint az ABC bármelyik más betűje. Ekkor már viszonylag hamar sikerült megtalálni, hogy minden olyan kódban, ahol használják, mellé is csomagolnak egy definíciót, mint például:
function $(el) { return document.getElementById(el); } 7.6 JavaScript patternek Mint minden nyelvben, a JavaScriptben is vannak patternek[5]. Ezek a nyelv eltérő filozófiájából adódóan eltérnek jó pár pontban a más nyelvektől megszokottaktól. Természetes módon itt mindent pattern felsorolása nem lehetséges, de pár áttekintésére vállalkozok, a hozzájuk kapcsolódó fogalmakkal egyetemben.
7.6.1 Kontextus A JavaScriptben a kontextus többféle képen is értelmezhető. Egyrészről, mivel az elemek dinamikusan típusozottak, ezért egy kifejezés kiértékelésekor meg kell állapítani, hogy az adott változó által mutatott memóraterületet milyen típusként kezeljen. Erre a következő érzékletes példa hozható (a → jelölje a kiértékelt eredményt): 19
Hámorszky Balázs
4 + 5 → 9 ”4” + 5 → 45 4 + ”5” → 45 4 + ”5” - 0 → 45 4 + ”5” + 0 → 450 4 + (”5” - 0) → 9 Ebből a példából is látszik, hogy a dinamikus típusok kikövetkeztetése rengeteg félreértéshez vezethet. A kontextus ugyanakkor más módon is értelmezhető. Ugyan azon függvény különböző módokon meghívva mást és mást jelenthet. Ezekben az esetekben a this kulcsszó különböző féle képpen oldódik.
7.6.1.1 Metódushívás Ha egy objektum metódusát hívjuk, a this kulcsszó az objektumra hivatkozik. Ez semmiben sem tér el a sok más nyelven megszokottól. A hivatkozás viselkedése teljesen megegyezik a Java-ban láthatótól.
myObject.myMethod(); meghívásánál a myMethod metódusban a this hivatkozás mindig a myObject referenciájára oldódik fel.
7.6.1.2 Függvényhívás Ha egy függvényben használjuk a this kulcsszót, a hivatkozás a világot jelentő objektumra mutat. Ez a szokásos esetben a window. Sokban eltér a JavaScript más megszokott nyelvektől abban, hogy a this mindenhol értelmezhető és, hogy van egy világobjektum, ami az alapértelmezettet adja a függvényhívásoknál a this-re. A világobjektum érdekessége, hogy minden létrejövő új objektum, akár közvetetten, akár közvetlenül a tagja lesz. Ezek alapján a, ha Webes környezetben meghívódik az előbb említett myMethod() úgy, hogy nem egy objektumon történik a rá való hivatkozás, abban az esetben, ha például átírja a this.title változót, akkor a Weboldal elnevezését változtatja meg.
20
Hámorszky Balázs
7.6.1.3 Konstruktor A this harmadik lehetséges jelentése a new kulcsszóval hozható elő. A JavaScript nem tesz különbséget konstruktor, metódus és függvény között. Hogy lehet így új objektumot konstruálni? A new kulcsszó bármilyen függvény előtt jelentősen megváltoztatja a függvény viselkedését. Ekkor nem csak új objektum jön létre a függvény hívásával, de a benne szereplő this már az új objektum példányára hivatkozik. Ez veszélyeket is rejthet magában. New nélkül az egyébként konstruktornak használt és adattagokat módosító vagy inicializáló függvény hirtelen a világobjektum adattagjait kezdi el módosítani. Nem követel magyarázatot, hogy milyen körültekintéssel kell eljárni a konstruktor függvények megírásánál és hívásánál.
7.6.1.4 Alkalmazott meghívás A függvények hívásának negyedik módja az alkalmazás. A függvényeket lehet egyszerű meghívás helyett alkalmazni (apply). Ebben az esetben, bár a függvény teljesen független az adott objektumtól, a benne szereplő this hivatkozások mind egytől egyig annak az objektumnak lesznek a referenciái, amire alkalmazva lett a függvény. Ezt a viselkedést a következő meghívási móddal lehet elérni:
fuggveny.apply(objektum, parameterek); 7.6.2 Closure A closure-ok fogalma manapság nagyon felkapott. A .NET környezet már egy ideje bír ezzel a tulajdonsággal, ameddig a Java még csak találgatni sem lehet, hogy végre melyik kiadásba tudják belerakni ezt a tulajdonságot. A lezártak (closure) röviden úgy foglalhatók össze, hogy a függvények a definíciójuk helyén látható környezetüket magukkal tudják vinni a későbbi végrehajtás helyére. Ez persze nem ész nélkül történik. A megoldás lényege, hogy a lezárásnál a nyelvi környezet figyelembe veszi azt, hogy mi volt hivatkozva abból a pontból. Lényegét tekintve ez egy megkönnyítése annak, hogy minden egyes alkalomra, amikor callback és delegált függvényeket ír a programozó, ne kelljen egyesével definiálnia azt, hogy milyen, az adott pontból látható referenciákat vigyen magával a függvény. Ez túlmutat az egyszerű (nyersen fordítva) szintaktikai cukorkákon (syntactic sugar), mivel egy-egy ilyen szituáció megoldása nem egyszerűen sokkal több gépelést jelent enélkül, de egyszersmind nyakatekert kerülőutakat is. Ez egy tipikusan olyan tulajdonság a modern nyelvekben, ami nélkül lehet élni, de nem 21
Hámorszky Balázs érdemes. Legkézenfekvőbb példája ennek az az igény, hogy az adott ablakból indított háttérfolyamat vissza tudják hívni az ablakot anélkül, hogy külön át kellett volna adni nekik az ablak referenciáját. Az igazán érdekes ebben a nyelvi elemben, hogy ameddig a nyelvek nagy része még csak most próbálkozik azzal, hogy ezt bevezesse vagy éppen, hogy csak be tudta vezetni, addig a JavaScriptnek az egyik alaptulajdonsága. A következőkben bemutatott pattern elsősorban ezt használja ki.
7.6.3 Lusta kiértékelés Az olyan nyelveknek, mint a mostanában egyre nagyobb népszerűséget szerző Haskell az egyik nagy vonzereje a lusta kiértékelés. A lustaság lényege, hogy ameddig nem kell, addig ne értékeljünk ki valamit, csak a referenciáját tartsuk észben, hogy rendelkezésre álljon, ha majd szükség lesz rá. Tipikusan egy mohó nyelven nem definiálhatunk végtelen sorokat, ameddig a Haskell vidáman adogatja őket körbe és csak, ha egy adott elemére van hivatkozás értékeli ki addig az elemig. A lusta kiértékelés ugyan nincsen beleégetve a JavaScriptbe, de meglepően egyszerűen elérhető. Fontos aspektus itt, hogy ha már egy adott függvénynek lekértük az eredményét, akkor tudjuk, hogy a későbbiekben már pontosan ugyan úgy fog viselkedni, mint az első lekéréskor, következésképpen fölösleges lenne minden egyes alkalommal kiszámolni a függvény visszatérési értékét, ehelyett vissza lehetne adni azonnal az első meghíváshoz tartozó visszatérési értéket. Klasszikusabb nyelveken, mint a C/C++/Java és társaik ezt úgy lehet elérni könnyedén, ahogy sokszor a singleton-okat is megvalósítják, mégpedig egy feltétel ellenőrzésével, hogy elsőre hívódott -e meg az adott függvény. Bár tagadhatatlan, hogy ez működhet így, de mégis egy rossz szájíz marad az emberben, hogy egy teljesen fölösleges feltétel ellenőrzés hajtódik végre az első után az össze további függvényhívásban. Nagyon szemléletes, mégis a determinisztikusságot figyelembe véve kissé suta példa, ha a lusta függvényünk azt az időpontot adja mindig vissza, amikor elsőre meghívták. A legnaivabb megoldás, ha egy dátum változó értékét ellenőrizzük és, ha definiálatlan, akkor adunk neki értéket. Ez persze, mivel egy szabad függvényről van szó azonnal egy globális változót jelent. A JavaScriptes megoldás ugyan nem megvalósíthatatlan még akár olyan alacsony szintű nyelvekben sem, mint a C, de nagyságrendekkel elegánsabb:
var foo = function() { 22
Hámorszky Balázs
var t = new Date(); foo = function() { return t; }; return foo(); }; A függvények teljesen egyenrangúak bármilyen más JavaScript objektummal (sőt, inkább ugyan azok), ezért miért ne definiálhatná felül egy névtelen függvény azt az egyetlen változót, ami rá mutat?
7.6.4 Module pattern A lezártak lehetőséget adnak a más nyelvekben module-okként ismert nyelvi elemek definiálására. Module alatt valami olyasmit értve, aminek van valamilyen publikus felülete és lehetnek saját adattagjai, ami kívülről nem látható. Kevéssé intuitív és nincsen nyelvi elemekkel alátámasztva a JavaScriptben (bár nyelvi lehetőségekkel annál inkább), de ez megoldható egy anonymus függvénnyel visszatérő anonymus függvénnyel.
var foo = (function() { var t; return function() { if (t) { return t; } t = new Date(); return t; } })(); A Mozilla Gecko motorja ugyanakkor ennél sokkal egyértelműbb megoldásokat tud nyújtani.
23
Hámorszky Balázs
7.7 Mozilla Gecko JavaScript kiterjesztések 7.7.1 Modulok A Mozilla a 3-as Firefox kiadására bevezetett egy új nyelvi elemet. A modulokat. A modulfájlok kiterjesztése némiképp eltér a klasszikus JavaScript fájlokétól, mivel .jsm-re végződnek, de ez nem jelent jelentős különbséget. A .jsm fájlok első látásra semmiben sem térnek el a .js kiterjesztésű társaiktól. Az egyetlen nagy differencia a definiált EXPORTED_SYMBOLS változó, általában a fájl elején. Ez a változó egy tömb és nem tartalmaz mást, mint a kívülről látható neveket.
var EXPORTED_SYMBOLS = ["foo", "bar"]; Az ilyen modulok írása tagadhatatlanul egyszerűbb, mint a Module Pattern használata és garantáltan, külön erőfeszítés nélkül rejti a nem exportált neveket. Az egyetlen nehézség, hogy a modulokat a .js fájloktól eltérően egy külön paranccsal kell betölteni.
Components.utils.import("resource://app/my_module .jsm"); 7.7.2 let Az újabb ECMAScript verziókban jelent meg, de még viszonylag kevesen implementálták a változók let-el való definiálását a var helyett. Más nyelvekhez képest a JavaScript szokatlanul szabadon kezeli a láthatóságot, így a blokk szülőblokkja látja a blokkon belül definiált változókat. Ezen a helyzeten próbál segíteni a let és nagyon hatékony eszköznek bizonyul. A let láthatósága egyrészről csak az adott blokkon belül érvényes, másrészt képes az elfedésre felüldefiniálás nélkül is, így akár egyetlen parancsra is meg lehet vele változtatni egy értéket, pontosan ugyan úgy, ahogy a Haskell monade-okban:
var a = 5; let(a = 6) alert(a); // 6 alert(a); // 5 7.7.3 const Hasonló ritka tulajdonság a konstansok megléte. A konstansok láthatóságban ugyan úgy viselkednek, mint a var-al definiált változók, azzal a különbséggel, hogy nem változók. Ez 24
Hámorszky Balázs alatt persze csak annyit szabad érteni, hogy nem állíthatók át más referenciára, mivel az általuk mutatott objektum belső állapotai továbbra is változtathatók.
7.7.4 XPCOM Az XPCOM-ról már ejtettem szót, de szeretném kicsivel részletesebben is tárgyalni. Ez persze csak a XULRunner-ben használt JavaScriptnek része, így nem használható bármilyen JavaScriptben[6]. Az XPCOM tehát egy általánosított mód arra, hogy más nyelven íródott részeket tudjunk betölteni a programba. Ellenérv lehet az XUL ellen üzleti alkalmazások fejlesztésekor, hogy a felület definíciója és a programot leíró JavaScript kód nem zárható le. Ez igaz, de a felület maga nagyon ritka esetben védendő része egy üzletileg terjesztett programnak. Legtöbb esetben az üzleti logikát szeretnénk védeni és ez az, amiért pénzt fizet a vásárló. Az XPCOM erre lehetősét ad. A következő nyelvekben lehet XPCOM-on keresztül betölteni komponenseket: •
C/C++
•
Java
•
Python
•
Ruby
•
JavaScript
Látható, hogy ezek közül van olyan, aminél lehetőség van csak bináris formátumú terjesztésre. Az XPCOM ugyanakkor ennél messzebbre megy. Nem csak be tudunk tölteni modulokat a program futása során, de azok úgy viselkednek, mint egy JavaScriptben létrehozott objektum, így azokból örökölni is tudnak új objektumok.
7.8 Kurrens JavaScript technológiák Nem tudom eleget hangsúlyozni, hogy mennyire hibás a JavaScript általános megítélése, mint egyszerű kis szkriptnyelv. Egyes körökben nagy slágernek számít és már jóformán mindenre használják.
7.8.1 NodeJS Egyik legjobb példája ennek a NodeJS, ami manapság egyre elterjedtebb szerver oldali programozásra. A NodeJS nem más, mint egy JavaScript futtatókörnyezet, mindenféle szép felület nélkül, elsősorban hálózatos alkalmazások megvalósításához. A JavaScript mostanra már nem csak, mint általános célú programozási nyelv tűnik fel, hanem 25
Hámorszky Balázs sok esetben gépi kódként tekintenek rá a programozók, mivel ez a webes standard programozási nyelv és az erre írt programok sokkal szélesebb felhasználói körhöz juttathatók el, mint bármilyen más nyelven íródott programok. Hogy használhatják ki ezt más nyelvek?
7.8.2 Emscripten Egyrészről olyan technológiákon keresztül, mint az Emscripten, ami nem más, mint az LLVM fordítóba becsatolható backend, ami ahelyett, hogy x86-os vagy ARM gépi kódra fordítaná a bemeneti programból a frontend által köztes reprezentációba átalakított programkódot, JavaScriptté alakítja azt. Tesztek szerint az így lefordított C program mindössze 10x lassabb, mint ugyan azon a gépen a gép saját gépi kódjára lefordított változat.
7.8.3 jslinux Hasonló próbálkozás a jslinux, ami egy teljes PC emulátor JavaScriptben megírva. Indításkor egy megszokott fekete ablakban kezdenek el benne megjelenni az ismerős Linux parancssoros üzenetek. Nem egy sebességbajnok, de működik. Van, aki Java virtuális gépet írd JavaScriptben, így Java programokat lehet futtatni a böngészőben módosítás nélkül, úgy, hogy nem kell telepíteni semmilyen Java beépülő modult a böngészőbe.
7.8.4 CoffeScript Ezeknél sokkal elegánsabb és általánosabb megoldások az olyan új nyelvek, mint a CoffeScript. Ennek az új nyelvnek nem más a célja, mint a JavaScript jó részeinek az elérhetővé tétele sokkal kevésbé lankásabb tanulási görbe mellett. A CoffeScript fordítója maga JavaScriptben van megírva és NodeJS-el lehet futtatni. A fordítás végén a CoffeScript kód a vele teljesen ekvivalens jelentéstartalommal rendelkező JavaScript kódra fordul, így a jelentősen leegyszerűsített szintaxis nem von maga után semmilyen futás idei költséget.
7.8.5 Coco A CoffeScript olyannyira sikeresnek bizonyult, hogy már arra is vette a valaki a fáradságot, hogy fork-olja. A Coco-nak nevezett nyelv fordítója tehát a CoffeScript fordítójának kódján alapul, de maga a nyelv némiképp meg lett változtatva. A készítő célja a szintaktika közelebb hozása az eredeti JavaScript/ECMAScript szemantikához, így a fordító rövidebb és jobban olvasható kódot tud generálni. Érdekesség, hogy külön kiemelt cél, hogy ameddig a 26
Hámorszky Balázs CoffeScript Ruby-ra hajaz inkább, a Coco a Perl felé próbál hajlani.
7.8.6 Asztali alkalmazások Mára a legtöbb asztali felhasználói felület egyszerűsített alkalmazásfejlesztési megoldásának kvázi sztenderdje lett a rendszer közeli nyelvek helyett a JavaScriptet biztosítani, mint a felhasználói felület logikájának megírási módja. Egyik legújabb példa erre a Qt Quick, amibe a Nokia is rengeteg pénzt fektetett (ameddig nem kötött szerződést a Microsoft-al és nem dobta ki az egészet a Windows Phone platform kedvéért). A Qt tipikusan C++-os megoldás volt mindig, de a Qt Quick egészen új ízt adott neki. A felület az XUL alkalmazásokhoz hasonlóan deklaratív módon adható meg, ameddig a viselkedés JavaScriptben programozható.
7.8.7 Futtatókörnyezet Hogy engedhetik meg maguknak ezeket az ambiciózus projekteket a készítőik? Pár évvel ezelőtt a JavaScript motorok éppen, hogy elég gyorsak voltak ahhoz, hogy a megfelelő kép fölé vitt egér hatására megváltoztassák a képet. Mára a web csurig van tömve interaktívabbnál interaktívabb alkalmazásokkal, amik sok esetben az asztali társaikat is lepipálják és az olyan nem triviális dolgok is, mint egy virtuális gép is úgy tud futni benne, hogy nem szenved el nagyságrendi lassulást. A válasz nem más, mint a verseny. Mióta a böngészők készítői gőzerőre kapcsoltak, hogy egymásra licitáljanak a JavaScript motorok sebességét mérő tesztekben, a fejlődés előtte elképzelhetetlen lendületet vett. Bár vannak szánalmas próbálkozások, mint a Microsoft-é, ami bizonyos tesztek végeredményét egyenesen a JavaScript motorba égette be, hogy jobb eredményeket érjen el, de ennek ellenére valós fejlődésről is beszélhetünk. A négy legnagyobb szereplő az Internet Explorer, a Firefox, a Google Chrome és az Opera. A felgyorsulást jól jelzi, hogy a Mozilla nemrégiben a Firefox kiadási ütemtervét némiképp a Google Chromehoz igazítva átállt a hat hetes kiadásokra. Ez sok embernek elvből nem tetszett, nekem személy szerint is meggyűlt vele a bajom. Ennek ellenére a verseny további felgyorsításához vezetett. A két legjobban pörgő kutatási terület (mert ezt egyértelműen nevezhető kutatásnak) a JIT és a Garbage Collector.
27
Hámorszky Balázs
7.8.7.1 JIT A JIT betűszó feloldása a Just In Time. A Just In Time a logisztikában is közkedvelt fogalom. Ott azért lényeges, mert a raktárterület az egyik legdrágább dolog. Ha a gyártósorhoz pont akkor érkezik be a gyárba az ülés, amikor azt be akarják szerelni, akkor sehol sem kellett raktározni és a kocsi pontosan ugyan olyan ütemben készül el, mintha sok ezer darabot tartott volna a gyár raktáron. A JavaScript futtatókörnyezetben (és sok más interpretált nyelvnél is) nagyon hasonló a jelentése. Sok esetben előfordulhat, hogy a programkód egy nagy része egyáltalán nem is kerül futtatásra, gondoljunk csak például arra az esetre, ha a program futása hibátlanul véget ér, de a program szépen robusztusra meg van írva és lépten-nyomon hibakezelő kóddal találja szembe magát, aki a forráskódot böngészgeti. Értelme van ilyen esetben a futás idei fordítónak a hibakezelő kódok lefordításával bajlódni? A JIT ugyanakkor ennél is jóval messzebbre mutató tulajdonságokkal ruházhatja fel a futtatókörnyezetet. Az egyik kurrens technológia, hogy a JIT úgynevezett követést végez a kódon és megpróbálja a leggyakrabban végrehajtott kódrészleteket detektálni, majd ha ezt megtette, ezeknek képes egy nagyon jól, a helyi architektúrára optimalizált változatát a memóriában tartani, így nagyságrendekkel felgyorsítva a futásidőt.
7.8.7.2 Garbage Collector Bár a fejlesztési idő az olyan nyelveknél, amik elrejtik a memóriakezelést a programozó elől, jelentősen tud gyorsulni, de ennek ára van. A memória felszabadítását a Garbage Collector végzi. A Microsoft a .NET környezetéhez tartozó Garbage Collector algoritmusának minden apró részletét féltve őrzi, ez jól példázza azt, hogy egy látszólag olyan egyszerű feladat, mint az üres memória újra kioszthatóságának biztosítása mennyire komoly feladat lehet. Az úgynevezett naiv Garbage Collector-ok úgy működnek, hogy végig számlálják az adott foglalt memóriaterületre mutató pointereket és ha olyan területet talál, amire nulla pointer mutat, akkor azt újra kioszthatóként bélyegzi meg. Ennek van egy nagyon alapvető problémája: a körkörös
hivatkozások
kiszűrésére:
a
referencia
számlálásos
módszerben
nincsen
determinisztikus futásidejű módszer. Ennek a problémának a megoldására születtek a gráfokon alapuló Garbage Collector-ok. Ahelyett, hogy az egyes elemeket egyenként számolná meg a memóriaterületek referenciáit, kiválaszt pár gyökér elemet és azokból kiindulva fut végig az összes referencián. Ha egy memória területre így nem talál referenciát, akkor az a memóraterület felszabadítható. Így a körkörös hivatkozások nem zavarhatják össze a memória felszabadítását. Ennek a memóriakezelő családnak egy lépéssel továbbvitele a generációs szemétgyűjtés. A 28
Hámorszky Balázs szemétgyűjtők készítői azt figyelték meg, hogy azok a memóriaterületek, amik már rég le lettek foglalva, sokáig le is maradnak foglalva, ahogy amik csak most lettek lefoglalva, legvalószínűbben elég hamar fel is szabadíthatóak lesznek. Ez a megfigyelés vezetett ahhoz, hogy próbáljuk a lehető legfrissebb referenciákat vizsgálni, mert ott lesz a leghamarabb felszabadítható memória is. Ide kapcsolódik a memória darabolódása is. Ha sok kis memóraterület közül pár hamar felszabadul, de emiatt sok sokáig megmaradó kis memóraterület marad körülöttük, így egy nagyobb lefoglalás nem fér el közéjük, tehát annak a program memóriájának a végére kerül, így rengeteg felhasználhatatlan üres memóra marad hátra. Itt egy új idő/tárterület „tradeoff” mutatkozik megoldásnak. Ha a szemétgyűjtés idejében a memóriát átrendezzük, akkor ugyan ez tovább tart, de kevesebb memóriát használunk később. A generációk közötti lépésnél a lefoglalt területek nem csak logikailag kerülnek át egyik generációból a másikba, hanem valósan is mozgatásra kerülnek, így a ritkán változó memóriaterületek ritkán lesznek átrendezve, így a költség minimalizálható. Ezek a módszerek persze csak a jéghegy csúcsai és rengeteg aprósággal lehet gyorsítani a szemétgyűjtést, ami, akármennyire gyorsul fel, mégis a program futásának leállításával jár időről időre. Furcsa lehet még, hogy olyan meglepő dolgokhoz vezethetnek a szemétgyűjtők, mint az, hogy az értékadás az egyik legköltségesebb műveletté válik. Ahogy a .NET futtatókörnyezetben is, az értékadásnál, ami nem jelent mást, mint egy referencia beállítását, a virtuális gép külön rutinokat tud lefuttatni, ami regisztrálja az új referenciát, így egy egyszerű értékadás, rengeteg programkód lefutásával járhat.
29
Hámorszky Balázs
8 Megvalósítás 8.1 XULRunner A futtatókörnyezet megismerése volt a következő nagy lépés. Ezzel is voltak már tapasztalataim, és a technológia maga annyira közel áll a webes megoldásokhoz, hogy a használatának elkezdése nem volt különösebben nehéz, de a webes fejlesztéshez képest a differencia elég nagy, hogy később viszont nehézségekbe is ütközzek. Az egész környezet teljes és átfogó ismerete talán csak egy-egy főfejlesztőjének lehet a kezében. A témakör magja persze maga a XUL nyelv, aminek a tanulási görbéjén a belépési szint meglehetősen alacsony, de itt is volt pár meglepő koncepció[7]. Ez a nyelv leginkább a HTML-re hasonlít. Nem csak abban, hogy XML mind a kettő, de az elemeinek egy részében is, illetve a rendeltetésében. A HTML is leginkább felület leírásra szolgál mostanában (bár a nevében is benne van, hogy eredetileg a szövegek megjelenítése volt az elsődleges célja).
8.2 Command Egyik ilyen furcsa koncepció volt a toolbarokhoz commandok kötése. Ahhoz szokott a webfejlesztésen nevelkedett ember, hogy a hasonló dolgokat onclick attribútum megadásával kezeli, de ebben az esetben ez nem így van, hanem egy commandsetet kell megadni:
Itt láthatóak a program jelenlegi commandjai. Ezek közül az utolsó már ebben a félévben kerül be. A kju menüpont hibakezelésre és debugolásra szolgál. A program futása közben fellépő hibákat és debug üzeneteket egy stringben gyűjtöm, mit utána egy új ablakban nyit meg a kju commandhoz rendelt openKju() függvény.
30
Hámorszky Balázs
8.3 Tree A tree elem alap szintű körbejárását is az előző félévben végeztem el. A tree elem alkalmas hierarchikus fák, de többoszlopos listák megjelenítésére is. Egy ilyen elem persze értelmetlen, ha nem lehet feltölteni adatokkal. Erre több lehetőség van. Ez is jól példázza, hogy mennyire szerteágazó lehetőségekkel rendelkezik ez a futtatókörnyezet. A legnaivabb hozzáállás a DOM elemek közvetlen módosítása[8]. Saját iterátorral a megjelenítendő adatok végigjárása és hozzáadása. Ez rengeteg kód megírásával jár, de szerencsére nincs rá szükség, vannak egyszerűbb megoldások is. A máshol is gyakran használt RDF formátum megjelenítése oldható meg a legkevesebb kódolással, de ez a megoldás merevebb, ezért a harmadik illik legjobban az adott feladathoz. Ez pedig az nsITreeView interfész. A XUL környezetben az interfészek teremtenek kapcsolatot komponensek között. Az interfészek nem csak JavaScript komponensekkel elégíthetők ki, hanem Java, C/C++ vagy Python komponensekkel is.
8.4 nsITreeView Az nsITreeView egy pár függvényt mindenképpen megkövetel az őt kielégítő objektumtól, és egy párat pedig megenged. Tudnia kell a tree-nek, hogy hány elemet kell megjelenítenie. Az így kapott countot felhasználva a tree lekéri a megfelelő elemek tartalmát celláról cellára. Ennek a kipróbálására egy könyvtár tartalmát jelenítettem meg a directory-service-en keresztül. Fájlok listájának olvasása A fájlrendszer elérése az nsIFile interfészen vagy ennek a kiterjesztett változatán, az nsILocalFile interfészen keresztül történik. A fájlok elérésének megkezdése előtt az XPCOM rendszertől le kell kérni a komponenst, ami végül az elérést biztosítani fogja. Ezt a következő módon lehet:
this.dserv = Components.classes["@mozilla.org/file/directory_s ervice;1"].getService(Components.interfaces.nsIPr operties); Ezek után a get metódussal meg lehet kapni egy könyvtárat:
this.path = this.dserv.get("Home", Components.interfaces.nsIFile); Az így megkapott könyvtárból ki lehet nyerni sorban a bejegyzéseket, amik praktikusan az 31
Hámorszky Balázs általa tartalmazott fájlok és könyvtárak:
var entries = this.path.directoryEntries; while(entries.hasMoreElements()) { var entry = entries.getNext(); entry. QueryInterface(Components.interfaces.nsIFile); if(entry.exists()) { this.displayData.push({ localname : entry.leafName, localsize : entry.fileSize, localdate : entry.lastModifiedTime, localattr : entry.permissions, icon : "moz-icon://" + entry.leafName + "?size=16" }); } }; A Tree felületi elem DOM megfelelőjének meg kell adni az nsITreeView interfészt kielégítő objektumot:
document.getElementById('localtree').view = new treeView(); Az inicializálás után az objektum getCellText metódusa hívódik meg, aminek elég ennyit tartalmaznia:
return this.displayData[row][col.id];
32
Hámorszky Balázs
2. ábra: Fájlok megjelenítése az nsITreeView interfészen keresztül
8.5 CSS A CSS vagy a feloldása, a Cascading Style Sheets talán jelenleg leggyakoribb felhasználása a HTML oldalak kinézetének szabályozása. Ez olyan mértékben igaz, hogy HTML újabb és újabb szabványaiból sorban távolítják el a HTML-ben klasszikusan használt formázási lehetőségeket. A teljesség igénye nélkül, a legfrissebb HTML5-ös szabványból eltávolították a következő elemeket (is): •
font: ezzel lehetett régebben megadni, hogy milyen betűtípust és betűméretet szeretnénk viszontlátni az oldalon
•
big: ami növelte a szöveg méretét
•
center: ami a középre húzást adta meg
•
u: aláhúzott szöveg
Itt is látszik, hogy a CSS már nem opció, hanem kvázi kényszer, ha az egyszeri webdizájner korszerű megjelenést akar kölcsönözni a keze alól kikerülő honlapnak. A program egyik felsorolt hiányossága volt, hogy nem volt képes a könyvtárakat ikonnal differenciálni a sima fájloktól. Az egyszerű fájlokhoz típus szerint már meg tudott jeleníteni különböző ikonokat. Linuxra és Windowsra is igaz, hogy ha feltelepül egy program, ami saját fájltípusokat használ, akkor általában hozzárendel a típushoz egy megnyitási parancsot, hogy az operációs rendszer felhasználói felületén a fájlt elindítva az adott programmal nyíljon meg a fájl. Emellett a hozzárendelés legtöbb esetben egyszersmind egy ikon hozzárendelését is 33
Hámorszky Balázs jelenti, tehát a fájl a fájlkezelőben egy a típusához vizuálisan könnyen társítható ikonnal jelenhet meg. Ezen ikonok elérésére egy nagyon kényelmes felületet biztosít a XULRunner környezet:
url(„moz-icon://" + filename + "?size=16") Ez az eljárás egyszerűen szöveg szerint fogja a fájl nevét és a fájl végződéséből kikövetkeztethető fájltípushoz hozzárendelt ikonnal tér vissza. Innen látható, hogy mivel a könyvtáraknak nincsen „könyvtár kiterjesztésük”, ezért nem rendelődik hozzájuk könyvtár ikon ezen a módon. Emellett fontos itt megemlíteni egy lényeges eltérést a Linux és a Windows hozzáállásában a rendszerikonokkal szemben. Amíg a Linux ikonjai könnyedén témázhatóak és bármelyik Linux felhasználó játszi könnyedséggel cserélgetheti a teljes ikontémát, addig a Windowsban bár elérhetőek olyan nem hivatalos segédeszközök, amelyek segítségével ez kivitelezhető, de semmiképpen sem nevezhető triviálisnak a használatuk. A helyzetet tovább nehezíti, hogy Windows alatt rendszergazdai jogosultságokkal kell rendelkezni egy ilyen művelet elvégzéséhez, hiszen az ikonok fontos rendszerfájlokba vannak beégetve, úgynevezett „erőforrás”-ként, tehát egyedül rendszerfájlok módosításával oldható meg az ikoncsere. Ez indikálta a fontos különbséget, hogy ameddig a Linuxon kívánatos a rendszer ikontémájához igazodás, addig ez Windowsban gyakorlatilag értelmezhetetlen, a XULRunner ezért nem ad egy standard felületet az adott elemek ikonjainak lekéréséhez. A téma tovább tárgyalásához be kell vezetni egy új fogalmat.
8.6 Chrome URL Egy programnak a futása során tudnia kell, hogy hol vannak azok a fájlok, amikből dolgozhat. Előre fordított nyelvek esetében ezt sokszor úgy oldják meg, hogy a program binárisa mellé csomagolódik fordítás időben a resource fájl. JavaScriptnél is lehet változókban tárolni fájlok tartalmát, de ez meglehetősen körülményes. A kézenfekvő megoldás, hogy megmondjuk a programnak, hogy hol keresheti az adott fájlt. Ezt URL-ek segítségével meg is lehet tenni, mint például[9]:
http://minta/eleresi/ut.html file:///minta/eleresi/ut.txt A file URL-ekkel minden lehetséges forgatókönyv szerint elérhetőek a helyben tárolt fájlok, de ha számításba vesszük azt, hogy szeretnénk, ha a program ne csak egy adott helyre legyen telepíthető a helyi fájlrendszeren, máris sokkal nehezebb az élet. 34
Hámorszky Balázs A Mozilla rendszerekben az élet megkönnyítésére kitalálták a chrome URL-ek koncepcióját. Ez nem más, mint egy plusz absztrakciós szint. A chrome.manifest fájlok szolgálnak ennek a külön hierarchiának a definiálásához. Ez sok ideig eléggé elvont és érthetetlen volt számomra, de elég utánaolvasás után kitisztult a kép. Az URL első szintjén egy név áll. Ez sok esetben a program vagy kiegészítő neve. A második szint a definiált tartalom típusa. A harmadik pedig a tartalom elérési útja. Egy nem kimerítő listája a lehetséges tartalmaknak: •
content
•
locale
•
skin
•
overlay
•
manifest
A program egy alap definíciós sora:
content
acmd
content/
Ez definiálja, hogy hol keresse a program a fájljait. Így a szkript definíciós .js fájlokat például így lehet meghívni a XUL fájlokban:
<script type="application/javascript" src="chrome://acmd/content/main.js"/> 8.7 Ikonok A könyvtárikonok problémájának feloldása tehát két lépésben tehető meg anélkül, hogy a JavaScript kódban kéne ellenőrizgetni az operációs rendszer típusát. Kívánatos lenne ezt CSSben kezelni, mivel szorosabban inkább a kinézethet kapcsolódik és nem lenne szép ezekkel elrondítani a program logikáját adó JavaScript kódot. A megoldás első lépéseként kihasználható, hogy a chrome.manifest sorai kondíciókhoz köthetők, így a következő sorokkal lefedhető az összes lehetséges XULRunner által támogatott operációs rendszer, kinézetük kezelésének szempontjából differenciálva:
skin
os-target
classic/1.0
skin/gnomestripe / os=Linux skin
os-target
classic/1.0
skin/pinstripe/ os=Darwin
35
Hámorszky Balázs
skin
os-target
classic/1.0
skin/winstripe/ os=WINNT osversion<6 skin
os-target
classic/1.0
skin/winstripe/ os=WINNT osversion>=6 skin
os-target
classic/1.0
skin/gnomestripe/ os!=WINNT skin
os-target
classic/1.0
skin/gnomestripe/ os=FreeBSD skin
os-target
classic/1.0
skin/gnomestripe/ os=OpenBSD Bár itt még nincs kihasználva, de már megvan a lehetőség a Windows ikonkészletében különböző verzióinak támogatása. Tehát az osversion<6 végű sorok XP-re, míg a osversion>=6 a Vista és 7 verziókra (Aero) lesz csak érvényes. Szerencsére erre csak akkor van szükség, ha saját ikont ad hozzá az ember a stílushoz, a beépítettek már alapból differenciálva vannak eszerint. Az idevágó sorai a XULRunner toolkit.manifest fájljának:
skin global classic/1.0 jar:toolkit.jar!/skin/classic/global/ os=WINNT osversion<6 skin global classic/1.0 jar:toolkit.jar!/skin/classic/aero/global/ os=WINNT osversion>=6 Itt látható az is, hogy a chrome URL-ek egy zip tömörített .jar fájl belsejébe is mutathatnak. A programban a platform specifikus CSS URL-je:
A második lépés a könyvtárikonok URL-jének meghatározása, majd a CSS megírása. Linux (és a hozzá hasonló UNIX alapú és szerű) rendszereknél a XULRunner GTK alapú, ezért a könyvtárikon GTK sémával kinyerhető:
list-style-image: url("moz-icon://stock/gtk36
Hámorszky Balázs
directory?size=16"); A Windowsos XULRunner tartalmaz a rendszer ikontémájához illeszkedő ikonokat, így azokat fel lehet használni. A könyvtárikonról azt kell tudni, hogy egy 6 ikont tartalmazó .png fájlban van, ezért ki kell vágni belőle a megfelelő részt:
list-style-image: url("chrome://global/skin/icons/folderitem.png"); -moz-image-region: rect(0px, 32px, 16px, 16px); Itt előjött az, hogy a Mozilla wikiben nincs dokumentálva a környezet minden aspektusa. A list-style-image csak más dokumentációkban található meg. Nekem nem volt triviális, hogy ezt kell egy ikon beállítására használni, mivel elsősorban a felsorolások ikonjának beállítására van kitalálva ez az attribútum a HTML esetében. Itt, felmerül a kérdés, hogy hogyan lehet beállítani egy ilyen tulajdonságot egy olyan elemnek, amit nem én helyezek el a felületre, hanem egy olyan automatizmus, ami felett nincs közvetlen kontrollom. Itt tűnt fel egy olyan fogalom a láthatáron, ami sehol sincs dokumentálva a Mozilla wikiben, bár nem mondom, hogy nincs megemlítve. Rengeteg helyen van a semmibe vezető kipirosított link az atomok definíciójára.
8.8 Atomok Ha nem tanultam volna a félév folyamán saját szorgalomból Erlangot, ezt a fogalmat sehova sem tudtam volna kötni. Ezt kivételes szerencsének gondolom, bár a megfejtése így is egy ideig fejtörést okozott. Minden változónak van neve. Az atom pont ezt tudja. Csak ezt. Kizárólag neve van. Hogy ennek mi értelme? Az igazság az, hogy minden, ami atomokkal elérhető, elérhető lenne boolean változókkal is, de ezen a ponton tovább is mehetünk, mert a boolean is reprezentálható 0-val és 1-el, vagy akár (ahogy a Perl esetében is), tekinthetünk bármilyen értéket logikainak. Az értelme igazából csak az, hogy megkönnyítse a programozó életét. Ha ebben az esetben egy adott cellához tulajdonságot szeretnénk rendelni (mint például, hogy könyvtárról vagy rejtett elemről van szó), meglehetősen kényelmetlen lenne felsorolni az összes lehetséges tulajdonságot és azokat true és false értékekre állítgatni. Látható, hogy ebben az esetben a kérdés egyedül az, hogy egy tulajdonság van, vagy nincs. Az nsITreeView interfész része a getCellProperties függvény, amin keresztül az egyes celláknak lehet
37
Hámorszky Balázs tulajdonságokat megadni. A tulajdonságokat atomok listájaként lehet visszaadni. Az atomokat egy „szolgáltatástól” lehet beszerezni:
this.gAtomService = Components.classes["@mozilla.org/atomservice;1"].getService(Components.interfaces.nsIA tomService); majdpedig:
this.gAtomService.getAtom("isFolder") Az így a cellához rendelt tulajdonságokra CSS-ben pedig így lehet hivatkozni:
.fileTree::-moz-tree-image(isFolder) 8.9 Kétpaneles elrendezés A kétpaneles elrendezés meglehetősen könnyen kialakítható volt. Ez annak köszönhető, hogy előre látó módon a lehető legnagyobb mértékben próbáltam általánosra írni a tree-t kiszolgáló objektumomat. Semmilyen esetben sem hivatkoztam elemre ID alapján. Ha a tree-ben valamilyen esemény kapcsán a dokumentum hierarchiában feljebb levő elemet kellett elérni, azt is parent és child-en keresztül tettem, nem a document node-ból indulva.
8.10 Lokalizálás A chrome URL-ekhez kapcsolódó témakör a lokalizálás. Manapság, hogy a számítógéphez való hozzáférés már nem kizárólag akadémikusok privilégiuma, kívánatossá vált a felhasználó nyelvére fordítani a programokat. Látható, hogy ez megint csak nem lenne szerencsés JavaScriptben kódolva. A megoldás most is két lépcsős volt. Első lépésnek a manifest-ben meg lehet adni az adott nyelvhez tartozó locale URL-t:
locale
acmd
en-US
locale/en-us/
locale
acmd
hu-HU
locale/hu-hu/
Így az URL nem aszerint lesz differenciálva, hogy milyen operációs rendszeren használják a programot, hanem, hogy milyen nyelvű operációs rendszeren (bár van lehetőség ráerőszakolni a kívánt nyelvet a programra). Az XUL fájlokban a fordítást tartalmazó fájl elérési útját:
Hámorszky Balázs
"chrome://acmd/locale/acmd.dtd"> a fordítani kívánt szöveget pedig:
a .dtd-ben:
Itt persze az avatottabb szemnek látszik, hogy szó sem volt egy külön fordítási rendszer kialakításáról. Teljesen szabványos DTD entitások segítségével van megoldva. A nyelvekhez való kötést egyszerűen a chrome URL rendszer végzi. Ez szerintem egészen kivételesen elegáns megoldás.
8.11 Navigálás a fájlrendszerben A fájlrendszerben való navigáláshoz elsőként egy nagyon fontos dolgot kellett megoldani, mégpedig az események kezelését. Az első lépés triviálisnak mondható:
A kérdés, hogy hogyan kapja meg a tree-t kezelő objektumom a kijelölést. Erre a válasz szerintem megint csak kifejezetten elegáns. Az öröklődésnek hála mindenféle külön definiálás nélkül elérhető a this.selection az objektum összes függvényéből. Az átláthatóság és a kódismétlés elkerülése végett csináltam egy könyvtárváltó függvényt:
this.getRawData(); this.tree.rowCountChanged(0, this.rowCount); this.tree.parentBox.getElementsByClassName('path' )[0].value=this.path.path; Az utolsó sor kiírja a felületen található szövegdobozba az aktuális könyvtárat. Az utolsó előtti frissíti a tree-t a felületen, az első pedig az adatokat frissíti.
8.12 Rendezés Ahogy azt már kiemeltem, a fájlrendszer listázása rendezés nélkül elég sete-suta. Itt is nagy szerencséje van a JavaScriptet használónak. Ha nem mazochista, nem kell megírnia a rendezési algoritmust. Ezzel persze nincs egyedül a JavaScript, de itt is elég a listára meghívni 39
Hámorszky Balázs a sort metódust és annak átadni egy függvényt, ami az összehasonlítást végzi. A nehézséget az okozta ebben, hogy az összehasonlító függvény nem paraméterezhető. A tree felületi elem felső részében a fejsor celláira kattintva meghívódik a cycleHeader függvény, amivel el lehet indítani a rendezést. Itt a probléma kettős. Egyrészről szeretnénk több szempontból rendezni, másrészt az a jó, ha a rendező függvény minél kevesebb döntést hoz és csak statikus memória hivatkozásokat hajt végre, mivel rengetegszer kerül meghívásra. Itt bonyolítja a helyzetet, hogy például a felületen megjelenő dátum nem az a dátum, ami szerint rendezni szeretnénk, mivel az már egy nehézkesen feldolgozható helyi dátumformátumban van, ezért a mögöttes epoch idő szerint sokkal hatékonyabb rendezni, ugyanakkor a header a lokalizált dátumhoz tartozik. A problémára a következő megoldást találtam:
if(col=="localdate"){ this.sortBy='rawdate'; } else{ this.sortBy=col; } majd a rendezőfüggvény, amit a sort eljárás hív egyszerűen:
function sortCmp(a, b){ if(a[sortBy] < b[sortBy]) return -1; if(a[sortBy] > b[sortBy]) return 1; return 0; }; Itt persze a környező kódban azzal is törődni kellett, hogy a felületen megjelenjen a rendezés iránya is.
40
Hámorszky Balázs
3. ábra: Fájlok listája név szerint rendezve, ikonokkal
8.13 Main Event Loop Minden grafikus alkalmazás lelke a központi eseménykezelő ciklus. Ez, azon ritka esetek egyike, amikor elvárjuk egy ciklustól, hogy ne egy számlálótól függjön, hogy hányszor kerül kiértékelésre a törzse, hanem végtelen ciklusként kell viselkednie, ami egy bizonyos feltétel fennállásától függően mégis kilép. Ha egy grafikus alkalmazást futtatunk, azt szeretnénk, hogy csak akkor álljon le a futása, ha mi kifejezetten erre instruáljuk a rendelkezésre álló parancsbeviteli metódusokkal. Hasonlóan fontos elvárás, hogy csináljon is valamit, ha erre adunk utasítást. Ezen elvárások teljesítésére a legbeváltabb módszer az eseménykezelő ciklus. A megoldás elsősorban két komponensből áll. Az egyik maga a ciklus, a másik egy várakozási sor, amibe az események kerülnek. Események származhatnak az alkalmazástól saját magától, de származhatnak az operációs rendszertől is. Az operációs rendszertől származó események elsősorban az egérkattintások és billentyűzetleütések, illetve a hálózati adatcsomagok lehetnek. Mikor új esemény kerül a várakozási sorba, a központi ciklus felébred az alvó állapotából és végrehajtja az esemény típusához tartozó elágazását. Ez modell jól bevált és már régóta szolgálja a felhasználói felülettel rendelkező alkalmazásokat. Egy nagy probléma azonban van vele: a lassú ki/bemeneti műveletek is beállnak a sorba és mikor végrehajtásra kerülnek, blokkolják a teljes program futását, ameddig véget nem érnek.
41
Hámorszky Balázs Gondot jelent még, hogy például egy fájlmásolás futása során így nem kap vizuális visszajelzést a felhasználó, hogy hol tart a másolás.
8.14 Szálkezelés Ennek a megoldására több lehetőség is van. Egyik, ami nem igényel különösebb módosítást az architektúrán, hogy a másolást kis darabokra bontjuk, majd minden egyes darab másolása után a következő darab másolását az eseménysorba helyezve lehetőséget adhatunk az egyéb események feldolgozására is. Ezzel ugyan visszajelzést lehet adni a felhasználó felé, hogy hol tart a másolás, de ha lemezhiba vagy hasonló miatt megakad a másolás, ugyan úgy megakasztja a teljes felhasználói felületet, ami nem kívánatos viselkedés. Lehetőség lehet még egy teljesen külön folyamat indítása az operációs rendszerben, ami viszont folyamatok közötti kommunikációt kíván és ennek megvalósítása nem egyszerű feladat és általában kiegészítő komponensek meglétét követeli meg. Az összes modern operációs rendszer ismeri a szálak fogalmát. Egy program futásakor egy úgynevezett „program counter” segítségével tudja az operációs rendszer, hogy hol tart a program által definiált parancsok futtatásában. A szálkezelés nem más, mint új PC-k bevezetése egy azon programon belül. Minden új szál kap saját stack-et és hozzáférhet a közös heap-hez, így a szálak közötti kommunikáció az osztott memórián keresztül megoldott.
8.15 Aszinkron másolás 8.15.1
Másolási ablak
A fájlkezelők többségében a másolás folyamatának követésére egy újabb ablak nyílik meg, amiben a folyamat előrehaladását általában egy a folyamat állásának megfelelő mértékben feltöltődő sávval tudja a felhasználó nyomon követni. Az ilyen ablakokat dialógusablakoknak szokás nevezni. Ahogy bármilyen ablaknál az XUL környezetben, ehhez is kell egy .xul fájl, ami az ablak kinézetét definiálja. Emellett szokásos módon a viselkedést egy .js fájlban lehet definiálni. A másolási ablaknak megnyílása után azonnal el kell kezdenie a másolást. Az ablakok rendelkeznek egy onload attribútummal, aminek egy függvényt lehet megadni és még az ablak kirajzolása előtt fut le. Ez azért jelent problémát, mert bár maga a másolás egy háttérszálon fut, hogy így ne blokkolja a felhasználói felületet, de az első fájl másolásáig így nem rajzolódik ki az ablak, mivel egészen addig nem ér véget az onload. Erre megoldás lehetne az onpaint attribútum, ami az ablak kirajzolása után hívódik meg, de sajnos ez jelenleg
42
Hámorszky Balázs nem működik és a Mozilla Developer Wikijének tanúsága szerint nem is valószín, hogy fog. Lehetséges még, hogy az onload nem maga kezdi el a másolást, csak elindít egy újabb szálat, minek mindössze annyi a dolga, hogy visszahívja a felületet futtató szálat, amint lehet, így az onload véget tud érni és a visszahívás már az ablak kirajzolása után történik meg. A legegyszerűbb módszer a szálak indítására a ChromeWorker-ek használata.
4. ábra: Másolási ablak
8.15.2
ChromeWorker
Egy ChromeWorker definiálásához egy új JavaScript fájlt kell létrehozni, ami az végrehajtandó „munka” leírását tartalmazza. Ebben az esetben a tartalma mindössze ennyi:
self.onmessage = function(event) { self.postMessage('Hi there!'); }; A ChromeWorker létrehozása és elindítása az onload metódusból pedig:
function onLoad() { var worker = new Worker('callback.js'); worker.onmessage = function(event){ doOp(); } worker.postMessage(''); } A doOp() függvény pedig már maga a másolást végző függvény, ami a másolási folyamatokat elindítja háttérszálakon. Felmerülhet a kérdés, hogy miért nem a ChromeWorker csinálja meg 43
Hámorszky Balázs a másolást, így nem kéne ennyi szálat indítani. A válasz sajnos az, hogy a ChromeWorker-ek nem férnek hozzá az XPCOM-hez (legalábbis a XULRunner jelenlegi verziójában nem) és mivel a fájlrendszer elérése XPCOM-on keresztül történik (natív komponensekkel), ezért erre nem használható.
8.15.3
Méret megállapítása
A másolás két lépésből áll. Hogy követhető legyen az effektív másolás folyamata, előtte meg kell állapítani a másolandó adatok méretét. Mivel a másolás forrása bármilyen (az operációs rendszer által megengedett) mélységű könyvtárstruktúra lehet, ezért ezt a struktúrát először be kell járni. A bejárás megvalósítására rekurzív algoritmust alkalmaztam. Az algoritmus minden megtalált fájl méretét hozzáadja egy szummához, valamint egy sort állít fel a fájlokból úgy, hogy a másolás, mozgatás és törlés is a helyes sorrendben hajtódhasson végre és ne állhasson elő olyan, hogy előbb próbál meg a program törölni egy könyvtárat, minthogy az teljesen kiürült volna. Másolás A másolást egy treeCopier elnevezésű objektum végzi. Ez az objektum nem csak elindítja egy háttérszálon az egyes fájlok másolását, de megvalósítja a nsIRequestObserver interfészt is, így a másolás után ezt az objektumot tudja közvetlenül visszahívni a nsIAsyncStreamCopier.
8.15.4
nsIAsyncStreamCopier
Az nsIAsyncStreamCopier, ahogy a neve is mutatja egy aszinkron adatfolyam másoló. Több paramétere között elvárj egy bemeneti és egy kimeneti adatfolyamot, amelyek közül legalább egyiknek puffereltnek kell lennie. Ezek mellett várja még egy szálnak az azonosítóját, ami ha null, akkor saját hatáskörben döntheti el, hogy melyik tetszés szerinti háttérszálon indítja el a másolást. Vár még egy Observer-t, ami ezesetben maga a treeCopier.
44
Hámorszky Balázs
5. ábra: Linuxon
6. ábra: Windowson
45
Hámorszky Balázs
9 Tervek a jövőre Egy szoftverre sohasem lehet azt mondani, hogy kész és tökéletes. Ameddig van felhasználója és akár egy fejlesztő, aki foglalkozik vele, addig a fejlesztése nem áll meg. Mindig kerülnek elő hibák és mindig kialakulhat igény új tulajdonságokra. Az elkészült fájlkezelő még fényévekre van lemaradva a vetélytársaitól mind funkcionalitásban, mind dizájnban. Első lépésnek a forráskódot szeretném elérhetővé tenni publikusan valamilyen szabad szoftver licenc alatt. Ez egyrészt a szoftver fejlődése szempontjával is hasznos hatásokkal járhat, másrészt jó alkalom kipróbálni, hogy milyen is részt venni egy szabad szoftver közösségi fejlesztésében. Funkcionalitások bővítése szempontjából a következők merülnek föl első nekifutásra: •
Hálózatos
fájlrendszerek
kezelése
Elméletileg a XULRunner minden szükséges eszközt biztosít ennek elérésére, mindössze egy megfelelően kitalált absztrakciós szintet kell beépíteni a fájlok elérése fölé, hogy ne kelljen egyedi esetként kezelni az összes különböző hálózatos és helyi fájlrendszer kezelését. •
Archívumok
kezelése
Az archívum fájlok (mint a ZIP) kezelése nem tér el sokban a hálózatos fájlrendszerek kezelésétől, az eszközök hasonló képen rendelkezésre állnak. •
Egyéb
kényelmi
szolgáltatások
A Total Commander-ből sokat lehet meríteni ilyen szempontból. Olyan extra funkciókkal segíti a felhasználást, mint keresés, ellenőrző összegek generálása, könyvtárak és fájlok összehasonlítása és még sok más. Ezeket mind megérné beépíteni.
46
Hámorszky Balázs
10 Összefoglaló A dolgozat céljául tűzte ki egy olyan prototípus projekt megvalósítását és elméleti hátterének bemutatását, ami jó alapot képezhet a későbbiekben egy teljes Linuxos asztali környezet megvalósításához egy fájlkezelő műveletek és a XULRunner projektek alapvető felépítésének feltárásával. A dolgozat elsőként a JavaScriptnek, mint programozási nyelvnek a bemutatására vállalkozott, itt kitérve olyan kevésbé közismert nyelvi koncepciók leírására, mint az osztály alapú nyelvek összehasonlítása a prototípus alapú megközelítéssel. Bemutatta a JavaScript futás idei kiértékeléséből fakadó félreérthetőséget, valamint a különféle kontextusok használatát. Ismertetésre kerültek bizonyos hasznos patternek és az olyan ezeket támogató nyelvi eszközök, mint a Closure-ok, Mozilla által nyújtott kiterjesztések a nyelvhez. Második lépésként az XUL technológia és a hozzá biztosított futtatókörnyezet, a XULRunner kerültek bemutatásra, a környezet részletes elemzésén és tulajdonságainak áttekintése révén. Végül a prototípus projekt felépítéséhez használt technológiák és futtatókörnyezet által kínált lehetőségek: •
A stílusozhatóság a platformfüggetlen környezetben platformfüggő módon.
•
A nyelvi beállításoktól függően a lokalizálási lehetőségek.
•
Az alkalmazás fejlesztése közben használható fejlesztői eszközöket és hibakeresési módszereket.
•
A fastruktúrák megjelenítése és az azokat támogató objektumok, illetve az azok helyett használható egyéb lehetőségek.
Fontos szerepet kapott a platformfüggetlen fájlműveletek körüljárása és azok a felhasználói felület blokkolása nélküli végrehajthatósága. A végeredményként létrejött alkalmazás képes biztosítani a fájlrendszerben való navigálást és olyan alapvető fájlműveletek elvégzését, mint a könyvtárlétrehozás, törlés, másolás és mozgatás. Maga az alkalmazás nagy potenciállal rendelkezik a továbbfejlesztési lehetőségek terén. Maga az alap felvetés az XUL alapú asztalkörnyezetre Linuxon egy lépéssel közelebb került, mivel ezeken az alapokon már el lehet indulni egy végeleges megvalósítás felé.
47
Hámorszky Balázs
Ábrajegyzék 1. ábra: Gecko működése.........................................................................................................12 2. ábra: Fájlok megjelenítése az nsITreeView interfészen keresztül.........................................33 3. ábra: Fájlok listája név szerint rendezve, ikonokkal.............................................................41 4. ábra: Másolási ablak............................................................................................................43 5. ábra: Linuxon.......................................................................................................................45 6. ábra: Windowson.................................................................................................................45
48
Hámorszky Balázs
Irodalomjegyzék [1] L. David Baron, Mozilla's Layout Engine, 2006, http://www.mozilla.org/newlayout/doc/layout-2006-12-14/master.xhtml [2] Florian Scholz, What XULRunner Provides, 2010, https://developer.mozilla.org/en/XULRunner/What_XULRunner_Provides [3] Alex Graveley, Main Page – Pyro Desktop, 2007, http://web.archive.org/web/20070818135349/http://pyrodesktop.org/Main_Page [4] Douglas Crockford, JavaScript: The Good Parts, Yahoo Press, 2008. [5] Stoyan Stefanov, JavaScript Patterns, O'Reilly Media, 2010. [6] Vaughn Bullard, Essential XUL Programming, Wiley, 2001. [7] Kenneth C. Feldt, Programming Firefox: Building Rich Internet Applications with XUL, O'Reilly Media, 2007. [8] Ray Harris, Murach's JavaScript and DOM Scripting (Murach: Training & Reference), Mike Murach & Associates, 2009. [9] Nigel McFarlane, Rapid Application Development with Mozilla, Prentice Hall, 2003.
49